001/*
002 * Copyright 2002-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.scheduling.quartz;
018
019import java.util.concurrent.Executor;
020import java.util.concurrent.RejectedExecutionException;
021
022import org.apache.commons.logging.Log;
023import org.apache.commons.logging.LogFactory;
024import org.quartz.SchedulerConfigException;
025import org.quartz.spi.ThreadPool;
026
027import org.springframework.lang.Nullable;
028import org.springframework.util.Assert;
029
030/**
031 * Quartz {@link ThreadPool} adapter that delegates to a Spring-managed
032 * {@link Executor} instance, specified on {@link SchedulerFactoryBean}.
033 *
034 * @author Juergen Hoeller
035 * @since 2.0
036 * @see SchedulerFactoryBean#setTaskExecutor
037 */
038public class LocalTaskExecutorThreadPool implements ThreadPool {
039
040        /** Logger available to subclasses. */
041        protected final Log logger = LogFactory.getLog(getClass());
042
043        @Nullable
044        private Executor taskExecutor;
045
046
047        @Override
048        public void setInstanceId(String schedInstId) {
049        }
050
051        @Override
052        public void setInstanceName(String schedName) {
053        }
054
055
056        @Override
057        public void initialize() throws SchedulerConfigException {
058                // Absolutely needs thread-bound Executor to initialize.
059                this.taskExecutor = SchedulerFactoryBean.getConfigTimeTaskExecutor();
060                if (this.taskExecutor == null) {
061                        throw new SchedulerConfigException("No local Executor found for configuration - " +
062                                        "'taskExecutor' property must be set on SchedulerFactoryBean");
063                }
064        }
065
066        @Override
067        public void shutdown(boolean waitForJobsToComplete) {
068        }
069
070        @Override
071        public int getPoolSize() {
072                return -1;
073        }
074
075
076        @Override
077        public boolean runInThread(Runnable runnable) {
078                Assert.state(this.taskExecutor != null, "No TaskExecutor available");
079                try {
080                        this.taskExecutor.execute(runnable);
081                        return true;
082                }
083                catch (RejectedExecutionException ex) {
084                        logger.error("Task has been rejected by TaskExecutor", ex);
085                        return false;
086                }
087        }
088
089        @Override
090        public int blockForAvailableThreads() {
091                // The present implementation always returns 1, making Quartz
092                // always schedule any tasks that it feels like scheduling.
093                // This could be made smarter for specific TaskExecutors,
094                // for example calling {@code getMaximumPoolSize() - getActiveCount()}
095                // on a {@code java.util.concurrent.ThreadPoolExecutor}.
096                return 1;
097        }
098
099}