Java tutorial
/* * 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.cocoon.thread.impl; import org.apache.cocoon.thread.RunnableManager; import org.apache.cocoon.thread.ThreadFactory; import org.apache.cocoon.thread.ThreadPool; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import EDU.oswego.cs.dl.util.concurrent.PooledExecutor; /** * The DefaultThreadPool class implements the {@link ThreadPool} interface. * Instances of this class are made by the {@link RunnableManager} passing a * threadpool into the <code>init</code> method. * * <pre> * <!--+ * | More indepth information can be found at * | http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/PooledExecutor.html * | The following elements can be used: * +--> * <!-- required name of the pool --> * <property name="name" value="default" /> * <!--+ * | optional priority all threads of the pool will have (the ThreadFactory will be * | set to this priority).The possible values are: * | MIN: corresponds to Thread#MIN_PRIORITY * | NORM: corresponds to Thread#NORM_PRIORITY (default) * | MAX: corresponds to Thread#MAX_PRIORITY * +--> * <property name="poolPriority" value="NORM" /> * <!--+ * | whether newly created Threads should run in daemon mode or not. Default to false. * +--> * <property name="daemon" value="false" /> * <!--+ * | optional size of a queue to hold Runnables if the pool is full. Possible values are: * | less than 0: unbounded (default) * | equal to 0: no queue at all * | greater than 0: size of the queue * --> * <property name="queueSize" value="-1" /> * <!--+ * | optional maximum number of threads in the pool. Defaults to 5. * | NOTE: if a queue is specified (queue-sie != 0) * | this value will be ignored. * +--> * <property name="maxPoolSize" value="5" /> * <!--+ * | optional minimum number of threads in the pool. Defaults to 5. * | NOTE: if a queue has been specified (queue-sie != 0) * | this value will be used as the maximum of thread running concurrently. * +--> * <property name="minPoolSize" value="5" /> * <!--+ * | The time in ms an idle thread should keep alive before it might get garbage collected. * | This defaults to 60000 ms. * +--> * <property name="keepAliveTime" value="60000" /> * <!--+ * | The policy to be used if all resources (thread in the pool and * | slots in the queue) are exhausted. * | Possible values are: * | ABORT: Throw a RuntimeException * | DISCARD: Throw away the current request and return. * | DISCARDOLDEST: Throw away the oldest request and return. * | RUN (default): The thread making the execute request runs the task itself. * | This policy helps guard against lockup. * | WAIT: Wait until a thread becomes available. This policy should, in * | general, not be used if the minimum number of threads is zero, * | in which case a thread may never become available. * +--> * <property name="blockPolicy" value="ABORT" /> * <!--+ * | Terminate thread pool after processing all Runnables currently in queue. Any * | Runnable entered after this point will be discarded. A shut down pool cannot * | be restarted. This also means that a pool will need keep-alive-time-ms to * | terminate. The default value not to shutdown graceful. * +--> * <property name="shutdownGraceful" value="false" /> * <!--+ * | The time in ms to wait before issuing an immediate shutdown after a graceful shutdown * | has been requested. * +--> * <property name="shutdownWaitTimeMs" value="-1" /> * <!--+ * | specifies the fully qualified class name of an org.apache.cocoon.thread.ThreadFactory * | implementation. It is responsible to create Thread classes. * +--> * <property name="factory" ref="defaultThreadFactory"/> * </pre> * * @version $Id: DefaultThreadPool.java 587751 2007-10-24 02:41:36Z vgritsenko $ */ public class DefaultThreadPool extends PooledExecutor implements ThreadPool { // ~ Static fields/initializers // --------------------------------------------- /** Default ThreadPool block policy */ public static final String POLICY_DEFAULT = POLICY_RUN; /** By default we use the logger for this class. */ protected final Log logger = LogFactory.getLog(getClass()); // ~ Instance fields // -------------------------------------------------------- /** The name of this thread pool */ private String name = ThreadPool.DEFAULT_THREADPOOL_NAME; /** Is daemon thread pool */ private boolean daemon = ThreadPool.DEFAULT_DAEMON_MODE; /** The priority of this thread pool */ private int priority = convertPriority(ThreadPool.DEFAULT_THREAD_PRIORITY); /** The maximum queue size */ private int queueSize = ThreadPool.DEFAULT_QUEUE_SIZE; /** The maximum pool size */ private int maxPoolSize = ThreadPool.DEFAULT_MAX_POOL_SIZE; /** The minimum pool size */ private int minPoolSize = ThreadPool.DEFAULT_MIN_POOL_SIZE; /** The keep alive time in milliseconds */ private long keepAliveTime = ThreadPool.DEFAULT_KEEP_ALIVE_TIME; /** The blocking policy */ private String blockPolicy = ThreadPool.DEFAULT_BLOCK_POLICY; /** Should we wait for running jobs to terminate on shutdown ? */ private boolean shutdownGraceful = ThreadPool.DEFAULT_SHUTDOWN_GRACEFUL; /** How long to wait for running jobs to terminate on disposition */ private int shutdownWaitTimeMs = ThreadPool.DEFAULT_SHUTDOWN_WAIT_TIME; /** A ThreadFactory implementation */ private ThreadFactory factory; /** Wrapps a channel */ private ChannelWrapper channelWrapper; /** The Queue */ private Queue queue; // ~ Constructors // ----------------------------------------------------------- /** * Create a new pool. * * @param channel * DOCUMENT ME! */ private DefaultThreadPool(final ChannelWrapper channel) { super(channel); channelWrapper = channel; } /** * Create a new pool. */ DefaultThreadPool() { this(new ChannelWrapper()); } // ~ Methods // ---------------------------------------------------------------- /** Initialize the bean after properties set */ public void init() throws IllegalArgumentException { if (logger.isInfoEnabled()) { logger.info("ThreadPool [" + name + "] initializing ..."); } initFactory(); this.setThreadFactory(factory); initMinPoolSize(); initPriority(); initDaemon(); initQueueSize(); initMaxPoolSize(); initKeepAliveTime(); if (logger.isInfoEnabled()) { logger.info(this.toString()); logger.info("ThreadPool [" + name + "] initialized"); } } /** * Get the block policy * * @return Returns the blockPolicy. */ public String getBlockPolicy() { return blockPolicy; } /** * DOCUMENT ME! * * @return maximum size of the queue (0 if isQueued() == false) * * @see org.apache.cocoon.thread.ThreadPool#getQueueSize() */ public int getMaxQueueSize() { return ((queueSize < 0) ? Integer.MAX_VALUE : queueSize); } /** * @see org.apache.cocoon.thread.ThreadPool#getName() */ public String getName() { return name; } /** * Get hte priority used to create Threads * * @return {@link Thread#MIN_PRIORITY}, {@link Thread#NORM_PRIORITY}, * or {@link Thread#MAX_PRIORITY} */ public int getPriority() { return ((ThreadFactory) super.getThreadFactory()).getPriority(); } /** * DOCUMENT ME! * * @return current size of the queue (0 if isQueued() == false) * * @see org.apache.cocoon.thread.ThreadPool#getQueueSize() */ public int getQueueSize() { return queue.getQueueSize(); } /** * Whether this DefaultThreadPool has a queue * * @return Returns the m_isQueued. * * @see org.apache.cocoon.thread.ThreadPool#isQueued() */ public boolean isQueued() { return queueSize != 0; } /** * Execute a command * * @param command * The {@link Runnable} to execute * * @throws InterruptedException * In case of interruption */ public void execute(Runnable command) throws InterruptedException { if (logger.isDebugEnabled()) { logger.debug("Executing Command: " + command.toString() + ",pool=" + getName()); } super.execute(command); } /** * @see org.apache.cocoon.thread.ThreadPool#shutdown() */ public void shutdown() { if (shutdownGraceful) { shutdownAfterProcessingCurrentlyQueuedTasks(); } else { shutdownNow(); } try { if (getShutdownWaitTimeMs() > 0) { if (!awaitTerminationAfterShutdown(getShutdownWaitTimeMs())) { logger.warn("running commands have not terminated within " + getShutdownWaitTimeMs() + "ms. Will shut them down by interruption"); interruptAll(); shutdownNow(); } } awaitTerminationAfterShutdown(); } catch (final InterruptedException ie) { logger.error("cannot shutdown ThreadPool", ie); } } /** * Set the blocking policy * * @param blockPolicy * The blocking policy value */ public void setBlockPolicy(final String blockPolicy) { this.blockPolicy = blockPolicy; if (POLICY_ABORT.equalsIgnoreCase(blockPolicy)) { abortWhenBlocked(); } else if (POLICY_DISCARD.equalsIgnoreCase(blockPolicy)) { discardWhenBlocked(); } else if (POLICY_DISCARD_OLDEST.equalsIgnoreCase(blockPolicy)) { discardOldestWhenBlocked(); } else if (POLICY_RUN.equalsIgnoreCase(blockPolicy)) { runWhenBlocked(); } else if (POLICY_WAIT.equalsIgnoreCase(blockPolicy)) { waitWhenBlocked(); } else { final StringBuffer msg = new StringBuffer(); msg.append("WARNING: Unknown block-policy configuration \"").append(blockPolicy); msg.append("\". Should be one of \"").append(POLICY_ABORT); msg.append("\",\"").append(POLICY_DISCARD); msg.append("\",\"").append(POLICY_DISCARD_OLDEST); msg.append("\",\"").append(POLICY_RUN); msg.append("\",\"").append(POLICY_WAIT); msg.append("\". Will use \"").append(POLICY_DEFAULT).append("\""); logger.warn(msg.toString()); setBlockPolicy(POLICY_DEFAULT); } } /** * DOCUMENT ME! * * @param name * The name to set. */ public void setName(String name) { this.name = name; } /** * DOCUMENT ME! * * @param shutdownGraceful * The shutdownGraceful to set. */ public void setShutdownGraceful(boolean shutdownGraceful) { this.shutdownGraceful = shutdownGraceful; } /** * DOCUMENT ME! * * @return Returns the shutdownGraceful. */ public boolean isShutdownGraceful() { return shutdownGraceful; } /** * DOCUMENT ME! * * @param shutdownWaitTimeMs * The shutdownWaitTimeMs to set. */ public void setShutdownWaitTimeMs(int shutdownWaitTimeMs) { this.shutdownWaitTimeMs = shutdownWaitTimeMs; } /** * DOCUMENT ME! * * @return Returns the shutdownWaitTimeMs. */ public int getShutdownWaitTimeMs() { return shutdownWaitTimeMs; } /** * @return the keepAliveTime */ public long getKeepAliveTime() { return keepAliveTime; } /** * @param keepAliveTime * the keepAliveTime to set */ public void setKeepAliveTime(long keepAliveTime) { this.keepAliveTime = keepAliveTime; } /** * @return the maxPoolSize */ public int getMaxPoolSize() { return maxPoolSize; } /** * @param maxPoolSize * the maxPoolSize to set */ public void setMaxPoolSize(int maxPoolSize) { this.maxPoolSize = maxPoolSize; } /** * @return the minPoolSize */ public int getMinPoolSize() { return minPoolSize; } /** * @param minPoolSize * the minPoolSize to set */ public void setMinPoolSize(int minPoolSize) { this.minPoolSize = minPoolSize; } /** * @return the threadFactory */ public ThreadFactory getFactory() { return factory; } /** * @param priority * the priority to set */ public void setPriority(int priority) { this.priority = priority; } /** * @param pool * priority the priority to set */ public void setPoolPriority(String poolPriority) { setPriority(convertPriority(poolPriority)); } /** * Sets the queue size of the thread pool * * @param queueSize * the queueSize to set */ public void setQueueSize(int queueSize) { this.queueSize = queueSize; } /** * Returns true if thread runs as daemon * * @return the daemon */ public boolean isDaemon() { return daemon; } /** * Set to true if thread shall run as daemon * * @param daemon * the daemon to set */ public void setDaemon(boolean daemon) { this.daemon = daemon; } /** * Overwrite the toString method */ public String toString() { if (this.isQueued()) { final StringBuffer msg = new StringBuffer(); msg.append("ThreadPool named \"").append(name); msg.append("\" created with maximum queue-size=").append(queueSize); msg.append(",max-pool-size=").append(maximumPoolSize_); msg.append(",min-pool-size=").append(minimumPoolSize_); msg.append(",priority=").append(priority); msg.append(",isDaemon=").append(((ThreadFactory) this.getThreadFactory()).isDaemon()); msg.append(",keep-alive-time-ms=").append(keepAliveTime_); msg.append(",block-policy=\"").append(blockPolicy); msg.append("\",shutdown-wait-time-ms=").append(shutdownWaitTimeMs); return msg.toString(); } else { final StringBuffer msg = new StringBuffer(); msg.append("ThreadPool named \"").append(name); msg.append("\" created with no queue,max-pool-size=").append(maximumPoolSize_); msg.append(",min-pool-size=").append(minimumPoolSize_); msg.append(",priority=").append(priority); msg.append(",isDaemon=").append(((ThreadFactory) this.getThreadFactory()).isDaemon()); msg.append(",keep-alive-time-ms=").append(keepAliveTime_); msg.append(",block-policy=\"").append(blockPolicy); msg.append("\",shutdown-wait-time-ms=").append(shutdownWaitTimeMs); return msg.toString(); } } /** * DOCUMENT ME! * * @param priority * The priority to set as string value. * * @return The priority as int value. */ private int convertPriority(final String priority) { if ("MIN".equalsIgnoreCase(priority)) { return Thread.MIN_PRIORITY; } else if ("NORM".equalsIgnoreCase(priority)) { return Thread.NORM_PRIORITY; } else if ("MAX".equalsIgnoreCase(priority)) { return Thread.MAX_PRIORITY; } else { logger.warn("Unknown thread priority \"" + priority + "\". Set to \"NORM\"."); return Thread.NORM_PRIORITY; } } /** * @param factory * the factory to set */ public void setFactory(ThreadFactory factory) { this.factory = factory; } private void initFactory() { if (factory == null) { logger.warn("No ThreadFactory is configured. Will use a " + DefaultThreadFactory.class.getName()); factory = new org.apache.cocoon.thread.impl.DefaultThreadFactory(); } } private void initMinPoolSize() { // Min pool size // make sure we have enough threads for the default thread pool as we // need one for ourself if (ThreadPool.DEFAULT_THREADPOOL_NAME.equals(name) && ((minPoolSize > 0) && (minPoolSize < ThreadPool.DEFAULT_MIN_POOL_SIZE))) { minPoolSize = ThreadPool.DEFAULT_MIN_POOL_SIZE; } if (minPoolSize < 1) { minPoolSize = (minPoolSize < 1) ? 1 : minPoolSize; logger.warn("min-pool-size < 1 for pool \"" + name + "\". Set to 1"); } super.setMinimumPoolSize(minPoolSize); } private void initPriority() { // Use priority from factory when changed priority = (factory.getPriority() != Thread.NORM_PRIORITY ? factory.getPriority() : priority); factory.setPriority(priority); } private void initDaemon() { // Use daemon from factory when changed daemon = (factory.isDaemon() != false ? factory.isDaemon() : daemon); factory.setDaemon(daemon); } private void initMaxPoolSize() { // Max pool size maxPoolSize = (maxPoolSize < 0) ? Integer.MAX_VALUE : maxPoolSize; super.setMaximumPoolSize(maxPoolSize); } private void initKeepAliveTime() { // Keep alive time if (keepAliveTime < 0) { keepAliveTime = 1000; logger.warn("keep-alive-time-ms < 0 for pool \"" + name + "\". Set to 1000"); } super.setKeepAliveTime(keepAliveTime); } private void initQueueSize() { if (queueSize != 0) { if (queueSize > 0) { queue = new BoundedQueue(queueSize); } else { queue = new LinkedQueue(); } } else { queue = new SynchronousChannel(); } channelWrapper.setChannel(queue); } }