Author: remm Date: Thu Nov 8 10:17:43 2018 New Revision: 1846116 URL: http://svn.apache.org/viewvc?rev=1846116&view=rev Log: Add a scheduled executor service to the Service, which can be used to process utility tasks including periodic ones. Add a simple wrapper to prevent random lifecycle and configuration operations. Add a bean for it.
Added: tomcat/trunk/java/org/apache/tomcat/util/threads/ScheduledThreadPoolExecutor.java (with props) Modified: tomcat/trunk/java/org/apache/catalina/Service.java tomcat/trunk/java/org/apache/catalina/core/StandardService.java tomcat/trunk/test/org/apache/catalina/mbeans/TestRegistration.java tomcat/trunk/webapps/docs/changelog.xml tomcat/trunk/webapps/docs/config/service.xml Modified: tomcat/trunk/java/org/apache/catalina/Service.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/Service.java?rev=1846116&r1=1846115&r2=1846116&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/Service.java (original) +++ tomcat/trunk/java/org/apache/catalina/Service.java Thu Nov 8 10:17:43 2018 @@ -18,6 +18,8 @@ package org.apache.catalina; +import java.util.concurrent.ScheduledExecutorService; + import org.apache.catalina.connector.Connector; import org.apache.catalina.mapper.Mapper; @@ -96,6 +98,20 @@ public interface Service extends Lifecyc public String getDomain(); + /** + * Get the utility thread count. + * @return the thread count + */ + public int getUtilityThreads(); + + + /** + * Set the utility thread count. + * @param utilityThreads the new thread count + */ + public void setUtilityThreads(int utilityThreads); + + // --------------------------------------------------------- Public Methods /** @@ -151,4 +167,10 @@ public interface Service extends Lifecyc * @return the mapper associated with this Service. */ Mapper getMapper(); + + /** + * @return the utility executor managed by the Service. + */ + ScheduledExecutorService getUtilityExecutor(); + } Modified: tomcat/trunk/java/org/apache/catalina/core/StandardService.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/StandardService.java?rev=1846116&r1=1846115&r2=1846116&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/core/StandardService.java (original) +++ tomcat/trunk/java/org/apache/catalina/core/StandardService.java Thu Nov 8 10:17:43 2018 @@ -20,6 +20,11 @@ package org.apache.catalina.core; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.ArrayList; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import javax.management.ObjectName; @@ -85,10 +90,25 @@ public class StandardService extends Lif private final Object connectorsLock = new Object(); /** - * + * The list of executors held by the service. */ protected final ArrayList<Executor> executors = new ArrayList<>(); + /** + * The number of threads available to process utility tasks in this service. + */ + protected int utilityThreads = 0; + + /** + * Utility executor with scheduling capabilities. + */ + private ScheduledThreadPoolExecutor utilityExecutor = null; + + /** + * Utility executor wrapper. + */ + private ScheduledExecutorService utilityExecutorWrapper = null; + private Engine engine = null; private ClassLoader parentClassLoader = null; @@ -202,8 +222,65 @@ public class StandardService extends Lif } + @Override + public int getUtilityThreads() { + return utilityThreads; + } + + + /** + * Handles the special values. + */ + private int getUtilityThreadsInternal() { + int result = getUtilityThreads(); + if (result > 0) { + return result; + } + + // Zero == Runtime.getRuntime().availableProcessors() + // -ve == Runtime.getRuntime().availableProcessors() + value + // These two are the same + result = (Runtime.getRuntime().availableProcessors() / 2) + result; + if (result < 1) { + result = 1; + } + return result; + } + + @Override + public void setUtilityThreads(int utilityThreads) { + if (utilityThreads < getUtilityThreadsInternal()) { + return; + } + int oldUtilityThreads = this.utilityThreads; + this.utilityThreads = utilityThreads; + + // Use local copies to ensure thread safety + if (oldUtilityThreads != utilityThreads && utilityExecutor != null) { + reconfigureUtilityExecutor(getUtilityThreadsInternal()); + } + } + + + private synchronized void reconfigureUtilityExecutor(int threads) { + if (utilityExecutor != null) { + utilityExecutor.setMaximumPoolSize(threads); + } else { + ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = + new ScheduledThreadPoolExecutor(1, new UtilityThreadFactory(getName() + "-utility-")); + scheduledThreadPoolExecutor.setMaximumPoolSize(threads); + scheduledThreadPoolExecutor.setKeepAliveTime(10, TimeUnit.SECONDS); + scheduledThreadPoolExecutor.setRemoveOnCancelPolicy(true); + scheduledThreadPoolExecutor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); + utilityExecutor = scheduledThreadPoolExecutor; + utilityExecutorWrapper = new org.apache.tomcat.util.threads.ScheduledThreadPoolExecutor(utilityExecutor); + } + } + + // --------------------------------------------------------- Public Methods + /** * Add a new Connector to the set of defined Connectors, and associate it * with this Service's Container. @@ -500,6 +577,7 @@ public class StandardService extends Lif executor.stop(); } } + } @@ -512,6 +590,9 @@ public class StandardService extends Lif super.initInternal(); + reconfigureUtilityExecutor(getUtilityThreadsInternal()); + register(utilityExecutor, "type=UtilityExecutor"); + if (engine != null) { engine.init(); } @@ -556,6 +637,12 @@ public class StandardService extends Lif engine.destroy(); } + if (utilityExecutor != null) { + utilityExecutor.shutdownNow(); + unregister("type=UtilityExecutor"); + utilityExecutor = null; + } + super.destroyInternal(); } @@ -613,4 +700,29 @@ public class StandardService extends Lif public final String getObjectNameKeyProperties() { return "type=Service"; } + + private static class UtilityThreadFactory implements ThreadFactory { + private final ThreadGroup group; + private final AtomicInteger threadNumber = new AtomicInteger(1); + private final String namePrefix; + + public UtilityThreadFactory(String namePrefix) { + SecurityManager s = System.getSecurityManager(); + group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); + this.namePrefix = namePrefix; + } + + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(group, r, namePrefix + threadNumber.getAndIncrement()); + thread.setDaemon(true); + return thread; + } + } + + @Override + public ScheduledExecutorService getUtilityExecutor() { + return utilityExecutorWrapper; + } + } Added: tomcat/trunk/java/org/apache/tomcat/util/threads/ScheduledThreadPoolExecutor.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/threads/ScheduledThreadPoolExecutor.java?rev=1846116&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/threads/ScheduledThreadPoolExecutor.java (added) +++ tomcat/trunk/java/org/apache/tomcat/util/threads/ScheduledThreadPoolExecutor.java Thu Nov 8 10:17:43 2018 @@ -0,0 +1,141 @@ +/* + * 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.tomcat.util.threads; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * Class which wraps a ScheduledExecutorService, while preventing + * lifecycle and configuration operations. + */ +public class ScheduledThreadPoolExecutor implements ScheduledExecutorService { + + protected final ScheduledExecutorService executor; + + /** + * Builds a wrapper for the given executor. + * @param executor the wrapped executor + */ + public ScheduledThreadPoolExecutor(ScheduledExecutorService executor) { + this.executor = executor; + } + + @Override + public void shutdown() { + // Do nothing + } + + + @Override + public List<Runnable> shutdownNow() { + return null; + } + + @Override + public boolean isShutdown() { + return executor.isShutdown(); + } + + @Override + public boolean isTerminated() { + return executor.isTerminated(); + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) + throws InterruptedException { + return executor.awaitTermination(timeout, unit); + } + + @Override + public <T> Future<T> submit(Callable<T> task) { + return executor.submit(task); + } + + @Override + public <T> Future<T> submit(Runnable task, T result) { + return executor.submit(task, result); + } + + @Override + public Future<?> submit(Runnable task) { + return executor.submit(task); + } + + @Override + public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) + throws InterruptedException { + return executor.invokeAll(tasks); + } + + @Override + public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, + TimeUnit unit) throws InterruptedException { + return executor.invokeAll(tasks, timeout, unit); + } + + @Override + public <T> T invokeAny(Collection<? extends Callable<T>> tasks) + throws InterruptedException, ExecutionException { + return executor.invokeAny(tasks); + } + + @Override + public <T> T invokeAny(Collection<? extends Callable<T>> tasks, + long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return executor.invokeAny(tasks, timeout, unit); + } + + @Override + public void execute(Runnable command) { + executor.execute(command); + } + + @Override + public ScheduledFuture<?> schedule(Runnable command, long delay, + TimeUnit unit) { + return executor.schedule(command, delay, unit); + } + + @Override + public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, + TimeUnit unit) { + return executor.schedule(callable, delay, unit); + } + + @Override + public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, + long initialDelay, long period, TimeUnit unit) { + return executor.scheduleAtFixedRate(command, initialDelay, period, unit); + } + + @Override + public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, + long initialDelay, long delay, TimeUnit unit) { + return executor.scheduleWithFixedDelay(command, initialDelay, delay, unit); + } + +} Propchange: tomcat/trunk/java/org/apache/tomcat/util/threads/ScheduledThreadPoolExecutor.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: tomcat/trunk/test/org/apache/catalina/mbeans/TestRegistration.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/mbeans/TestRegistration.java?rev=1846116&r1=1846115&r2=1846116&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/catalina/mbeans/TestRegistration.java (original) +++ tomcat/trunk/test/org/apache/catalina/mbeans/TestRegistration.java Thu Nov 8 10:17:43 2018 @@ -72,6 +72,7 @@ public class TestRegistration extends To "Tomcat:type=Server", "Tomcat:type=Service", "Tomcat:type=StringCache", + "Tomcat:type=UtilityExecutor", "Tomcat:type=Valve,name=StandardEngineValve", }; } Modified: tomcat/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1846116&r1=1846115&r2=1846116&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Thu Nov 8 10:17:43 2018 @@ -64,6 +64,10 @@ Include German translations for the Manager application in the standard Tomcat distribution. (markt) </fix> + <add> + Add a scheduled executor to the Service, which can be used to + process periodic utility tasks. (remm) + </add> </changelog> </subsection> </section> Modified: tomcat/trunk/webapps/docs/config/service.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/service.xml?rev=1846116&r1=1846115&r2=1846116&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/config/service.xml (original) +++ tomcat/trunk/webapps/docs/config/service.xml Thu Nov 8 10:17:43 2018 @@ -68,6 +68,17 @@ must be unique.</p> </attribute> + <attribute name="startStopThreads" required="false"> + <p>The number of threads this <strong>Service</strong> will use for + various utility tasks, including recurring ones. The special value + of 0 will result in the value of + <code>Runtime.getRuntime().availableProcessors()/2</code> being + used. Negative values will result in + <code>Runtime.getRuntime().availableProcessors()/2 + value</code> being + used unless this is less than 1 in which case 1 thread will be used. + </p> + </attribute> + </attributes> </subsection> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org