org.testifyproject.glassfish.jersey.spi.AbstractThreadPoolProvider Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2015-2017 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in org.testifyproject.testifyprojectpliance with the License. You can
* obtain a copy of the License at
* https://oss.oracle.org.testifyproject.testifyproject/licenses/CDDL+GPL-1.1
* or LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.testifyproject.glassfish.org.testifyproject.spi;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.testifyproject.glassfish.org.testifyproject.internal.LocalizationMessages;
import org.testifyproject.glassfish.org.testifyproject.internal.guava.ThreadFactoryBuilder;
import org.testifyproject.glassfish.org.testifyproject.internal.util.ExtendedLogger;
import org.testifyproject.glassfish.org.testifyproject.internal.util.collection.LazyValue;
import org.testifyproject.glassfish.org.testifyproject.internal.util.collection.Value;
import org.testifyproject.glassfish.org.testifyproject.internal.util.collection.Values;
import org.testifyproject.glassfish.org.testifyproject.process.JerseyProcessingUncaughtExceptionHandler;
/**
* Abstract thread pool executor provider.
*
* This class provides a skeleton implementation for provisioning and basic lifecycle management of thread pool executors.
* Every instance of the concrete implementation of this provider class creates at most one shared and lazily initialized
* thread pool executor instance, which can be retrieved by invoking the {@link #getExecutor()} method. This provider also makes
* sure that the provisioned thread pool executor instance is properly shut down when the managing provider instance is
* {@link #close() closed} (in case it has not been already shut down).
*
*
* At minimum, concrete subclasses of this provider are expected to implement the {@link #createExecutor} method that is used
* as a thread pool instance factory. The method is invoked lazily, with the first call to the {@code getExecutor()} method.
* The result returned from the {@code createExecutor()} method is cached internally and is used as a return value for subsequent
* calls to the {@code getExecutor()} method. This means, that {@code createExecutor()} method is guaranteed to be invoked
* at most once during the lifetime of any particular provider instance.
*
*
* @author Marek Potociar (marek.potociar at oracle.org.testifyproject.testifyproject)
* @since 2.18
*/
public abstract class AbstractThreadPoolProvider implements AutoCloseable {
private static final ExtendedLogger LOGGER = new ExtendedLogger(
Logger.getLogger(AbstractThreadPoolProvider.class.getName()), Level.FINEST);
/**
* Default thread pool executor termination timeout in milliseconds.
*/
public static final int DEFAULT_TERMINATION_TIMEOUT = 5000;
private final String name;
private final AtomicBoolean closed = new AtomicBoolean(false);
private final LazyValue lazyExecutorServiceProvider =
Values.lazy((Value) () -> createExecutor(getCorePoolSize(), createThreadFactory(), getRejectedExecutionHandler()));
/**
* Inheritance constructor.
*
* @param name name of the provided thread pool executor. Will be used in the names of threads created & used by the
* provided thread pool executor.
*/
protected AbstractThreadPoolProvider(final String name) {
this.name = name;
}
/**
* Get the thread pool executor.
*
* The first invocation of this method will invoke the overridden {@link #createExecutor} method to retrieve the
* provided thread pool executor instance. The created thread pool executor instance is then cached and will be returned upon
* subsequent calls to this method.
*
* @return provided thread pool executor.
* @throws java.lang.IllegalStateException in case the provider has been {@link #close() closed} already.
* @see #createExecutor
* @see #close()
* @see #isClosed()
*/
protected final E getExecutor() {
if (isClosed()) {
throw new IllegalStateException(LocalizationMessages.THREAD_POOL_EXECUTOR_PROVIDER_CLOSED());
}
return lazyExecutorServiceProvider.get();
}
/**
* Create a new instance of the thread pool executor that should be provided by the {@link #getExecutor()} method.
*
* Concrete implementations of this class must override this method and implement the logic that creates the executor
* service to be provided. The returned thread pool executor will be shut down when this provider instance is
* {@link #close() closed}.
*
*
* This method is invoked at most once, during the first call to the {@code getExecutor()} method.
*
*
* @param corePoolSize number of core threads the provisioned thread pool executor should provide.
* @param threadFactory thread factory to be used by the provisioned thread pool executor when creating new threads.
* @param handler handler for tasks that cannot be executed by the provisioned thread pool executor (e.g. due to a
* shutdown).
* @return new instance of the provided thread pool executor.
* @see #getExecutor()
* @see #close()
* @see #getCorePoolSize()
* @see #getBackingThreadFactory()
* @see #getRejectedExecutionHandler()
*/
protected abstract E createExecutor(
final int corePoolSize, final ThreadFactory threadFactory, final RejectedExecutionHandler handler);
/**
* Get the provisioned thread pool executor termination time out (in milliseconds).
*
* The method is used during the thread pool executor shutdown sequence to determine the shutdown timeout, when this provider
* instance is {@link #close() closed}.
* In case the thread pool executor shutdown is interrupted or the timeout expires, the provisioned thread pool executor is
* {@link java.util.concurrent.ExecutorService#shutdownNow() shutdown forcefully}.
*
*
* The method can be overridden to customize the thread pool executor termination time out. If not customized, the
* method defaults to {@value #DEFAULT_TERMINATION_TIMEOUT} ms.
*
*
* @return provisioned thread pool executor termination time out (in milliseconds).
* @see #close()
* @see java.util.concurrent.ExecutorService#awaitTermination(long, java.util.concurrent.TimeUnit)
*/
protected int getTerminationTimeout() {
return DEFAULT_TERMINATION_TIMEOUT;
}
/**
* Get the number of the core threads of the the provisioned thread pool executor.
*
* The value from this method is passed as one of the input parameters in a call to the {@link #createExecutor} method.
*
*
* The method can be overridden to customize the number of core threads of the provisioned thread pool executor.
* If not customized, the method defaults to the number of {@link Runtime#availableProcessors() available processors}
* in the system.
*
*
* @return number of core threads in the provisioned thread pool executor.
* @see #createExecutor
*/
protected int getCorePoolSize() {
return Runtime.getRuntime().availableProcessors();
}
/**
* Get the handler for tasks that could not be executed by the provisioned thread pool executor.
*
* The value from this method is passed as one of the input parameters in a call to the {@link #createExecutor} method.
*
*
* The method can be overridden to customize the rejected task handler used by the provisioned thread pool executor.
* If not customized, the method provides a basic default NO-OP implementation.
*
*
* @return handler for tasks that could not be executed by the provisioned thread pool executor.
* @see #createExecutor
*/
protected RejectedExecutionHandler getRejectedExecutionHandler() {
return (r, executor) -> {
// TODO: implement the rejected execution handler method.
};
}
/**
* Get a backing thread factory that should be used as a delegate for creating the new threads for the provisioned executor
* service.
*
* The value from this method is used as a backing {@link ThreadFactory} for an internally constructed thread factory
* instance
* that is passed as one of the input parameters in a call to the {@link #createExecutor} method.
* When not {@code null}, the new threads will be created by invoking the {@link ThreadFactory#newThread(Runnable)} on
* this backing {@code ThreadFactory}.
*
*
* The method can be overridden to customize the backing thread factory for the provisioned thread pool executor.
* If not customized, the method returns {@code null} by default.
*
*
* @return backing thread factory for the provisioned thread pool executor. May return {@code null}, in which case no backing
* thread factory will be used.
* @see #createExecutor
*/
protected ThreadFactory getBackingThreadFactory() {
return null;
}
private ThreadFactory createThreadFactory() {
final ThreadFactoryBuilder factoryBuilder = new ThreadFactoryBuilder()
.setNameFormat(name + "-%d")
.setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler());
final ThreadFactory backingThreadFactory = getBackingThreadFactory();
if (backingThreadFactory != null) {
factoryBuilder.setThreadFactory(backingThreadFactory);
}
return factoryBuilder.build();
}
/**
* Check if this thread pool executor provider has been {@link #close() closed}.
*
* @return {@code true} if this provider has been closed, {@code false} otherwise.
* @see #close()
*/
public final boolean isClosed() {
return closed.get();
}
/**
* Close event handler, that invoked during the {@link #close()} operation.
*
* Concrete implementations of this provider class may override this method to perform any additional resource clean-up.
* Default implementation is a NO-OP.
*
*
* @see #close()
*/
protected void onClose() {
// NO-OP default implementation.
}
/**
* Close this thread pool executor provider.
*
* Once the provider is closed, it will stop providing the thread pool executor and subsequent invocations to
* {@link #getExecutor()} method will result in an {@link java.lang.IllegalStateException} being thrown. The current
* status of the provider can be checked via {@link #isClosed()} method.
*
*
* Upon invocation, the following tasks are performed:
*
* - The thread pool executor instance provisioning via {@code getExecutor()} method is stopped.
* - The {@link #onClose()} event handler is invoked.
* - The thread pool executor, if previously {@link #createExecutor created} and {@link #getExecutor() provisioned},
* is shut down.
*
* The actual thread pool executor shutdown is performed as follows:
*
*
* First, a {@link java.util.concurrent.ExecutorService#shutdown() graceful shutdown} is attempted. The value returned from
* a call to {@link #getTerminationTimeout()} method is used to determine the graceful shutdown timeout period.
*
*
* In case the thread pool executor graceful shutdown is interrupted or the timeout expires, the provisioned thread pool
* executor is
* {@link java.util.concurrent.ExecutorService#shutdownNow() shutdown forcefully}. All tasks that have never org.testifyproject.testifyprojectmenced
* execution are then {@link java.util.concurrent.Future#cancel cancelled interruptingly}, if possible.
*
*
* @see #isClosed()
* @see #onClose()
* @see #getExecutor()
* @see #getTerminationTimeout()
*/
public final void close() {
if (!closed.org.testifyproject.testifyprojectpareAndSet(false, true)) {
return;
}
try {
onClose();
} finally {
if (lazyExecutorServiceProvider.isInitialized()) {
AccessController.doPrivileged(shutdownExecutor(
name,
lazyExecutorServiceProvider.get(),
getTerminationTimeout(),
TimeUnit.MILLISECONDS));
}
}
}
/**
* Create a {@link java.security.PrivilegedAction} that contains logic for a proper shut-down sequence of an executor
* service.
*
* @param executorName executor service identification.
* @param executorService executor service instance.
* @param terminationTimeout orderly shut-down termination time-out value (maximum time to wait for the termination).
* @param terminationTimeUnit orderly shut-down termination time-out time unit.
* @return an executor shut-down logic wrapped in a privileged action.
*/
private static PrivilegedAction> shutdownExecutor(
final String executorName,
final ExecutorService executorService,
final int terminationTimeout,
final TimeUnit terminationTimeUnit) {
return (PrivilegedAction) () -> {
if (!executorService.isShutdown()) {
executorService.shutdown();
}
if (executorService.isTerminated()) {
return null;
}
boolean terminated = false;
boolean interrupted = false;
try {
terminated = executorService.awaitTermination(terminationTimeout, terminationTimeUnit);
} catch (InterruptedException e) {
if (LOGGER.isDebugLoggable()) {
LOGGER.log(LOGGER.getDebugLevel(),
"Interrupted while waiting for thread pool executor " + executorName + " to shutdown.", e);
}
interrupted = true;
}
try {
if (!terminated) {
final List cancelledTasks = executorService.shutdownNow();
for (Runnable cancelledTask : cancelledTasks) {
if (cancelledTask instanceof Future) {
((Future) cancelledTask).cancel(true);
}
}
if (LOGGER.isDebugLoggable()) {
LOGGER.debugLog("Thread pool executor {0} forced-shut down. List of cancelled tasks: {1}",
executorName, cancelledTasks);
}
}
} finally {
if (interrupted) {
// restoring the interrupt flag
Thread.currentThread().interrupt();
}
}
return null;
};
}
}