All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opencms.scheduler.CmsSchedulerThreadPool Maven / Gradle / Ivy

Go to download

OpenCms is an enterprise-ready, easy to use website content management system based on Java and XML technology. Offering a complete set of features, OpenCms helps content managers worldwide to create and maintain beautiful websites fast and efficiently.

There is a newer version: 17.0
Show newest version
/*
 * This library is part of OpenCms -
 * the Open Source Content Management System
 *
 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * For further information about Alkacon Software GmbH & Co. KG, please see the
 * company website: http://www.alkacon.com
 *
 * For further information about OpenCms, please see the
 * project website: http://www.opencms.org
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *
 * This library is based to some extend on code from the
 * OpenSymphony Quartz project. Original copyright notice:
 *
 * Copyright James House (c) 2001-2005
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met: 1.
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer. 2. Redistributions in
 * binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other
 * materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.opencms.scheduler;

import org.opencms.main.CmsLog;

import org.apache.commons.logging.Log;

import org.quartz.SchedulerConfigException;
import org.quartz.spi.ThreadPool;

/**
 * Simple thread pool used for the Quartz scheduler in OpenCms.

* * @since 6.0.0 */ public class CmsSchedulerThreadPool implements ThreadPool { /** The log object for this class. */ private static final Log LOG = CmsLog.getLog(CmsSchedulerThreadPool.class); /** The current thread count. */ private int m_currentThreadCount; /** The inheri group. */ private boolean m_inheritGroup; /** The inherit loader. */ private boolean m_inheritLoader; /** The initial thread count. */ private int m_initialThreadCount; /** Flag indicating if the system is shutting down. */ private boolean m_isShutdown; /** Flag indicating whether to create thread deamons. */ private boolean m_makeThreadsDaemons; /** The max thread count. */ private int m_maxThreadCount; /** The next runnable. */ private Runnable m_nextRunnable; /** The next runnable lock. */ private Object m_nextRunnableLock; /** The thread group. */ private ThreadGroup m_threadGroup; /** The thread name prefix. */ private String m_threadNamePrefix; /** The thread priority. */ private int m_threadPriority; /** The threads. */ private CmsSchedulerThread[] m_workers; /** * Create a new CmsSchedulerThreadPool with default values. * * This will create a pool with 0 initial and 10 maximum threads running * in normal priority.

* * @see #CmsSchedulerThreadPool(int, int, int) */ public CmsSchedulerThreadPool() { this(0, 10, Thread.NORM_PRIORITY); } /** * Create a new CmsSchedulerThreadPool with the specified number * of threads that have the given priority. * * The OpenCms scheduler thread pool will initially start with provided number of * active scheduler threads. * When a thread is requested by the scheduler, and no "free" threads are available, * a new thread will be added to the pool and used for execution. The pool * will be allowed to grow until it has reached the configured number * of maximum threads.

* * @param initialThreadCount the initial number of threads for the pool * @param maxThreadCount maximum number of threads the pool is allowed to grow * @param threadPriority the thread priority for the scheduler threads * * @see java.lang.Thread */ public CmsSchedulerThreadPool(int initialThreadCount, int maxThreadCount, int threadPriority) { m_inheritGroup = true; m_inheritLoader = true; m_nextRunnableLock = new Object(); m_threadNamePrefix = "OpenCms: Scheduler Thread "; m_makeThreadsDaemons = true; m_initialThreadCount = initialThreadCount; m_currentThreadCount = 0; m_maxThreadCount = maxThreadCount; m_threadPriority = threadPriority; } /** * @see org.quartz.spi.ThreadPool * * @return if the pool should be blocked for available threads */ public int blockForAvailableThreads() { // Waiting will be done in runInThread so we always return 1 return 1; } /** * @see org.quartz.spi.ThreadPool#getPoolSize() */ public int getPoolSize() { return m_currentThreadCount; } /** * Returns the thread priority of the threads in the scheduler pool.

* * @return the thread priority of the threads in the scheduler pool */ public int getThreadPriority() { return m_threadPriority; } /** * @see org.quartz.spi.ThreadPool#initialize() */ public void initialize() throws SchedulerConfigException { if ((m_maxThreadCount <= 0) || (m_maxThreadCount > 200)) { throw new SchedulerConfigException(Messages.get().getBundle().key(Messages.ERR_MAX_THREAD_COUNT_BOUNDS_0)); } if ((m_initialThreadCount < 0) || (m_initialThreadCount > m_maxThreadCount)) { throw new SchedulerConfigException(Messages.get().getBundle().key(Messages.ERR_INIT_THREAD_COUNT_BOUNDS_0)); } if ((m_threadPriority <= 0) || (m_threadPriority > 9)) { throw new SchedulerConfigException( Messages.get().getBundle().key(Messages.ERR_SCHEDULER_PRIORITY_BOUNDS_0)); } if (m_inheritGroup) { m_threadGroup = Thread.currentThread().getThreadGroup(); } else { // follow the threadGroup tree to the root thread group m_threadGroup = Thread.currentThread().getThreadGroup(); ThreadGroup parent = m_threadGroup; while (!parent.getName().equals("main")) { m_threadGroup = parent; parent = m_threadGroup.getParent(); } m_threadGroup = new ThreadGroup(parent, this.getClass().getName()); } if (m_inheritLoader) { LOG.debug( Messages.get().getBundle().key( Messages.LOG_USING_THREAD_CLASSLOADER_1, Thread.currentThread().getName())); } // create the worker threads and start them m_workers = new CmsSchedulerThread[m_maxThreadCount]; for (int i = 0; i < m_initialThreadCount; ++i) { growThreadPool(); } } /** * Run the given Runnable object in the next available * Thread.

* * If while waiting the thread pool is asked to * shut down, the Runnable is executed immediately within a new additional * thread.

* * @param runnable the Runnable to run * @return true if the Runnable was run */ public boolean runInThread(Runnable runnable) { if (runnable == null) { return false; } if (m_isShutdown) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_THREAD_POOL_UNAVAILABLE_0)); return false; } boolean hasNextRunnable; synchronized (m_nextRunnableLock) { // must synchronize here to avoid potential double checked locking hasNextRunnable = (m_nextRunnable != null); } if (hasNextRunnable || (m_currentThreadCount == 0)) { // try to grow the thread pool since other runnables are already waiting growThreadPool(); } synchronized (m_nextRunnableLock) { // wait until a worker thread has taken the previous Runnable // or until the thread pool is asked to shutdown while ((m_nextRunnable != null) && !m_isShutdown) { try { m_nextRunnableLock.wait(1000); } catch (InterruptedException e) { // can be ignores } } // during normal operation, not shutdown, set the nextRunnable // and notify the worker threads waiting (getNextRunnable()) if (!m_isShutdown) { m_nextRunnable = runnable; m_nextRunnableLock.notifyAll(); } } // if the thread pool is going down, execute the Runnable // within a new additional worker thread (no thread from the pool) // note: the synchronized section should be as short (time) as // possible as starting a new thread is not a quick action if (m_isShutdown) { CmsSchedulerThread thread = new CmsSchedulerThread( this, m_threadGroup, m_threadNamePrefix + "(final)", m_threadPriority, false, runnable); thread.start(); } return true; } /** * Terminate any worker threads in this thread group.

* * Jobs currently in progress will be allowed to complete.

*/ public void shutdown() { shutdown(true); } /** * Terminate all threads in this thread group.

* * @param waitForJobsToComplete if true,, all current jobs will be allowed to complete */ public void shutdown(boolean waitForJobsToComplete) { m_isShutdown = true; // signal each scheduler thread to shut down for (int i = 0; i < m_currentThreadCount; i++) { if (m_workers[i] != null) { m_workers[i].shutdown(); } } // give waiting (wait(1000)) worker threads a chance to shut down // active worker threads will shut down after finishing their // current job synchronized (m_nextRunnableLock) { m_nextRunnableLock.notifyAll(); } if (waitForJobsToComplete) { // wait until all worker threads are shut down int alive = m_currentThreadCount; while (alive > 0) { alive = 0; for (int i = 0; i < m_currentThreadCount; i++) { if (m_workers[i].isAlive()) { try { if (LOG.isDebugEnabled()) { LOG.debug( Messages.get().getBundle().key(Messages.LOG_THREAD_POOL_WAITING_1, new Integer(i))); } // note: with waiting infinite - join(0) - the application // may appear to 'hang' // waiting for a finite time however requires an additional loop (alive) alive++; m_workers[i].join(200); } catch (InterruptedException e) { // can be ignored } } } } int activeCount = m_threadGroup.activeCount(); if ((activeCount > 0) && LOG.isInfoEnabled()) { LOG.info( Messages.get().getBundle().key(Messages.LOG_THREAD_POOL_STILL_ACTIVE_1, new Integer(activeCount))); } if (LOG.isDebugEnabled()) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_THREAD_POOL_SHUTDOWN_0)); } } } /** * Dequeue the next pending Runnable.

* * @return the next pending Runnable * @throws InterruptedException if something goes wrong */ protected Runnable getNextRunnable() throws InterruptedException { Runnable toRun = null; // Wait for new Runnable (see runInThread()) and notify runInThread() // in case the next Runnable is already waiting. synchronized (m_nextRunnableLock) { if (m_nextRunnable == null) { m_nextRunnableLock.wait(1000); } if (m_nextRunnable != null) { toRun = m_nextRunnable; m_nextRunnable = null; m_nextRunnableLock.notifyAll(); } } return toRun; } /** * Grows the thread pool by one new thread if the maximum pool size * has not been reached.

*/ private void growThreadPool() { if (m_currentThreadCount < m_maxThreadCount) { // if maximum number is not reached grow the thread pool synchronized (m_nextRunnableLock) { m_workers[m_currentThreadCount] = new CmsSchedulerThread( this, m_threadGroup, m_threadNamePrefix + m_currentThreadCount, m_threadPriority, m_makeThreadsDaemons); m_workers[m_currentThreadCount].start(); if (m_inheritLoader) { m_workers[m_currentThreadCount].setContextClassLoader( Thread.currentThread().getContextClassLoader()); } // increas the current size m_currentThreadCount++; // notify the waiting threads m_nextRunnableLock.notifyAll(); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy