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.camel.impl; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.apache.camel.CamelContext; import org.apache.camel.ThreadPoolRejectedPolicy; import org.apache.camel.model.OptionalIdentifiedDefinition; import org.apache.camel.model.ProcessorDefinition; import org.apache.camel.model.ProcessorDefinitionHelper; import org.apache.camel.model.RouteDefinition; import org.apache.camel.spi.ExecutorServiceStrategy; import org.apache.camel.spi.LifecycleStrategy; import org.apache.camel.spi.ThreadPoolProfile; import org.apache.camel.util.ObjectHelper; import org.apache.camel.util.concurrent.ExecutorServiceHelper; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * @version $Revision$ */ public class DefaultExecutorServiceStrategy extends ServiceSupport implements ExecutorServiceStrategy { private static final Log LOG = LogFactory.getLog(DefaultExecutorServiceStrategy.class); private final List<ExecutorService> executorServices = new ArrayList<ExecutorService>(); private final CamelContext camelContext; private String threadNamePattern; private String defaultThreadPoolProfileId; private final Map<String, ThreadPoolProfile> threadPoolProfiles = new HashMap<String, ThreadPoolProfile>(); public DefaultExecutorServiceStrategy(CamelContext camelContext) { this.camelContext = camelContext; // create and register the default profile this.defaultThreadPoolProfileId = "defaultThreadPoolProfile"; ThreadPoolProfile defaultProfile = new ThreadPoolProfileSupport(defaultThreadPoolProfileId); // the default profile has the following values defaultProfile.setDefaultProfile(true); defaultProfile.setPoolSize(10); defaultProfile.setMaxPoolSize(20); defaultProfile.setKeepAliveTime(60L); defaultProfile.setTimeUnit(TimeUnit.SECONDS); defaultProfile.setMaxQueueSize(1000); defaultProfile.setRejectedPolicy(ThreadPoolRejectedPolicy.CallerRuns); registerThreadPoolProfile(defaultProfile); } public void registerThreadPoolProfile(ThreadPoolProfile profile) { threadPoolProfiles.put(profile.getId(), profile); } public ThreadPoolProfile getThreadPoolProfile(String id) { return threadPoolProfiles.get(id); } public ThreadPoolProfile getDefaultThreadPoolProfile() { return getThreadPoolProfile(defaultThreadPoolProfileId); } public void setDefaultThreadPoolProfile(ThreadPoolProfile defaultThreadPoolProfile) { ThreadPoolProfile oldProfile = threadPoolProfiles.remove(defaultThreadPoolProfileId); if (oldProfile != null) { // the old is no longer default oldProfile.setDefaultProfile(false); // fallback and use old default values for new default profile if absent (convention over configuration) if (defaultThreadPoolProfile.getKeepAliveTime() == null) { defaultThreadPoolProfile.setKeepAliveTime(oldProfile.getKeepAliveTime()); } if (defaultThreadPoolProfile.getMaxPoolSize() == null) { defaultThreadPoolProfile.setMaxPoolSize(oldProfile.getMaxPoolSize()); } if (defaultThreadPoolProfile.getRejectedPolicy() == null) { defaultThreadPoolProfile.setRejectedPolicy(oldProfile.getRejectedPolicy()); } if (defaultThreadPoolProfile.getMaxQueueSize() == null) { defaultThreadPoolProfile.setMaxQueueSize(oldProfile.getMaxQueueSize()); } if (defaultThreadPoolProfile.getPoolSize() == null) { defaultThreadPoolProfile.setPoolSize(oldProfile.getPoolSize()); } if (defaultThreadPoolProfile.getTimeUnit() == null) { defaultThreadPoolProfile.setTimeUnit(oldProfile.getTimeUnit()); } } // validate that all options has been given as its mandatory for a default thread pool profile // as it is used as fallback for other profiles if they do not have that particular value ObjectHelper.notEmpty(defaultThreadPoolProfile.getId(), "id", defaultThreadPoolProfile); ObjectHelper.notNull(defaultThreadPoolProfile.getKeepAliveTime(), "keepAliveTime", defaultThreadPoolProfile); ObjectHelper.notNull(defaultThreadPoolProfile.getMaxPoolSize(), "maxPoolSize", defaultThreadPoolProfile); ObjectHelper.notNull(defaultThreadPoolProfile.getMaxQueueSize(), "maxQueueSize", defaultThreadPoolProfile); ObjectHelper.notNull(defaultThreadPoolProfile.getPoolSize(), "poolSize", defaultThreadPoolProfile); ObjectHelper.notNull(defaultThreadPoolProfile.getTimeUnit(), "timeUnit", defaultThreadPoolProfile); LOG.info("Using custom DefaultThreadPoolProfile: " + defaultThreadPoolProfile); // and replace with the new default profile this.defaultThreadPoolProfileId = defaultThreadPoolProfile.getId(); // and mark the new profile as default defaultThreadPoolProfile.setDefaultProfile(true); registerThreadPoolProfile(defaultThreadPoolProfile); } public String getThreadName(String name) { return ExecutorServiceHelper.getThreadName(threadNamePattern, name); } public String getThreadNamePattern() { return threadNamePattern; } public void setThreadNamePattern(String threadNamePattern) { // must set camel id here in the pattern and let the other placeholders be resolved by ExecutorServiceHelper String name = threadNamePattern.replaceFirst("\\$\\{camelId\\}", camelContext.getName()); this.threadNamePattern = name; } public ExecutorService lookup(Object source, String name, String executorServiceRef) { ExecutorService answer = camelContext.getRegistry().lookup(executorServiceRef, ExecutorService.class); if (answer != null && LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) { LOG.debug("Looking up ExecutorService with ref: " + executorServiceRef + " and found it from Registry: " + answer); } } if (answer == null) { // try to see if we got a thread pool profile with that id answer = newThreadPool(source, name, executorServiceRef); if (answer != null && LOG.isDebugEnabled()) { LOG.debug("Looking up ExecutorService with ref: " + executorServiceRef + " and found a matching ThreadPoolProfile to create the ExecutorService: " + answer); } } return answer; } public ScheduledExecutorService lookupScheduled(Object source, String name, String executorServiceRef) { ScheduledExecutorService answer = camelContext.getRegistry().lookup(executorServiceRef, ScheduledExecutorService.class); if (answer != null && LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) { LOG.debug("Looking up ScheduledExecutorService with ref: " + executorServiceRef + " and found it from Registry: " + answer); } } if (answer == null) { ThreadPoolProfile profile = getThreadPoolProfile(name); if (profile != null) { int poolSize = profile.getPoolSize(); answer = newScheduledThreadPool(source, name, poolSize); if (answer != null && LOG.isDebugEnabled()) { LOG.debug("Looking up ScheduledExecutorService with ref: " + executorServiceRef + " and found a matching ThreadPoolProfile to create the ScheduledExecutorService: " + answer); } } } return answer; } public ExecutorService newDefaultThreadPool(Object source, String name) { ThreadPoolProfile profile = getDefaultThreadPoolProfile(); ObjectHelper.notNull(profile, "DefaultThreadPoolProfile"); return newThreadPool(source, name, profile.getPoolSize(), profile.getMaxPoolSize(), profile.getKeepAliveTime(), profile.getTimeUnit(), profile.getMaxQueueSize(), profile.getRejectedExecutionHandler(), false); } public ExecutorService newThreadPool(Object source, String name, String threadPoolProfileId) { ThreadPoolProfile defaultProfile = getDefaultThreadPoolProfile(); ThreadPoolProfile profile = getThreadPoolProfile(threadPoolProfileId); if (profile != null) { // fallback to use values from default profile if not specified Integer poolSize = profile.getPoolSize() != null ? profile.getPoolSize() : defaultProfile.getPoolSize(); Integer maxPoolSize = profile.getMaxPoolSize() != null ? profile.getMaxPoolSize() : defaultProfile.getMaxPoolSize(); Long keepAliveTime = profile.getKeepAliveTime() != null ? profile.getKeepAliveTime() : defaultProfile.getKeepAliveTime(); TimeUnit timeUnit = profile.getTimeUnit() != null ? profile.getTimeUnit() : defaultProfile.getTimeUnit(); Integer maxQueueSize = profile.getMaxQueueSize() != null ? profile.getMaxQueueSize() : defaultProfile.getMaxQueueSize(); RejectedExecutionHandler handler = profile.getRejectedExecutionHandler() != null ? profile.getRejectedExecutionHandler() : defaultProfile.getRejectedExecutionHandler(); // create the pool return newThreadPool(threadPoolProfileId, source, name, poolSize, maxPoolSize, keepAliveTime, timeUnit, maxQueueSize, handler, false); } else { // no profile with that id return null; } } public ExecutorService newCachedThreadPool(Object source, String name) { ExecutorService answer = ExecutorServiceHelper.newCachedThreadPool(threadNamePattern, name, true); onThreadPoolCreated(answer, source, null); if (LOG.isDebugEnabled()) { LOG.debug("Created new cached thread pool for source: " + source + " with name: " + name + ". -> " + answer); } return answer; } public ScheduledExecutorService newScheduledThreadPool(Object source, String name) { int poolSize = getDefaultThreadPoolProfile().getPoolSize(); return newScheduledThreadPool(source, name, poolSize); } public ScheduledExecutorService newScheduledThreadPool(Object source, String name, int poolSize) { ScheduledExecutorService answer = ExecutorServiceHelper.newScheduledThreadPool(poolSize, threadNamePattern, name, true); onThreadPoolCreated(answer, source, null); if (LOG.isDebugEnabled()) { LOG.debug("Created new scheduled thread pool for source: " + source + " with name: " + name + ". [poolSize=" + poolSize + "]. -> " + answer); } return answer; } public ExecutorService newFixedThreadPool(Object source, String name, int poolSize) { ExecutorService answer = ExecutorServiceHelper.newFixedThreadPool(poolSize, threadNamePattern, name, true); onThreadPoolCreated(answer, source, null); if (LOG.isDebugEnabled()) { LOG.debug("Created new fixed thread pool for source: " + source + " with name: " + name + ". [poolSize=" + poolSize + "]. -> " + answer); } return answer; } public ExecutorService newSingleThreadExecutor(Object source, String name) { ExecutorService answer = ExecutorServiceHelper.newSingleThreadExecutor(threadNamePattern, name, true); onThreadPoolCreated(answer, source, null); if (LOG.isDebugEnabled()) { LOG.debug("Created new single thread pool for source: " + source + " with name: " + name + ". -> " + answer); } return answer; } public ExecutorService newSynchronousThreadPool(Object source, String name) { ExecutorService answer = ExecutorServiceHelper.newSynchronousThreadPool(); onThreadPoolCreated(answer, source, null); if (LOG.isDebugEnabled()) { LOG.debug("Created new synchronous thread pool for source: " + source + " with name: " + name + ". -> " + answer); } return answer; } public ExecutorService newThreadPool(Object source, String name, int corePoolSize, int maxPoolSize) { ExecutorService answer = ExecutorServiceHelper.newThreadPool(threadNamePattern, name, corePoolSize, maxPoolSize); onThreadPoolCreated(answer, source, null); if (LOG.isDebugEnabled()) { LOG.debug("Created new thread pool for source: " + source + " with name: " + name + ". [poolSize=" + corePoolSize + ", maxPoolSize=" + maxPoolSize + "] -> " + answer); } return answer; } public ExecutorService newThreadPool(Object source, String name, int corePoolSize, int maxPoolSize, int maxQueueSize) { ExecutorService answer = ExecutorServiceHelper.newThreadPool(threadNamePattern, name, corePoolSize, maxPoolSize, maxQueueSize); onThreadPoolCreated(answer, source, null); if (LOG.isDebugEnabled()) { LOG.debug("Created new thread pool for source: " + source + " with name: " + name + ". [poolSize=" + corePoolSize + ", maxPoolSize=" + maxPoolSize + ", maxQueueSize=" + maxQueueSize + "] -> " + answer); } return answer; } private ExecutorService newThreadPool(String threadPoolProfileId, Object source, String name, int corePoolSize, int maxPoolSize, long keepAliveTime, TimeUnit timeUnit, int maxQueueSize, RejectedExecutionHandler rejectedExecutionHandler, boolean daemon) { // the thread name must not be null ObjectHelper.notNull(name, "ThreadName"); // If we set the corePoolSize to be 0, the whole camel application will hang in JDK5 // just add a check here to throw the IllegalArgumentException if (corePoolSize < 1) { throw new IllegalArgumentException("The corePoolSize can't be lower than 1"); } ExecutorService answer = ExecutorServiceHelper.newThreadPool(threadNamePattern, name, corePoolSize, maxPoolSize, keepAliveTime, timeUnit, maxQueueSize, rejectedExecutionHandler, daemon); onThreadPoolCreated(answer, source, threadPoolProfileId); if (LOG.isDebugEnabled()) { LOG.debug("Created new thread pool for source: " + source + " with name: " + name + ". [poolSize=" + corePoolSize + ", maxPoolSize=" + maxPoolSize + ", keepAliveTime=" + keepAliveTime + " " + timeUnit + ", maxQueueSize=" + maxQueueSize + ", rejectedExecutionHandler=" + rejectedExecutionHandler + ", daemon=" + daemon + "] -> " + answer); } return answer; } public ExecutorService newThreadPool(Object source, String name, int corePoolSize, int maxPoolSize, long keepAliveTime, TimeUnit timeUnit, int maxQueueSize, RejectedExecutionHandler rejectedExecutionHandler, boolean daemon) { return newThreadPool(null, source, name, corePoolSize, maxPoolSize, keepAliveTime, timeUnit, maxQueueSize, rejectedExecutionHandler, daemon); } public void shutdown(ExecutorService executorService) { ObjectHelper.notNull(executorService, "executorService"); if (executorService.isShutdown()) { return; } if (LOG.isDebugEnabled()) { LOG.debug("Shutdown ExecutorService: " + executorService); } executorService.shutdown(); if (LOG.isTraceEnabled()) { LOG.trace("Shutdown ExecutorService: " + executorService + " complete."); } } public List<Runnable> shutdownNow(ExecutorService executorService) { ObjectHelper.notNull(executorService, "executorService"); if (executorService.isShutdown()) { return null; } if (LOG.isDebugEnabled()) { LOG.debug("ShutdownNow ExecutorService: " + executorService); } List<Runnable> answer = executorService.shutdownNow(); if (LOG.isTraceEnabled()) { LOG.trace("ShutdownNow ExecutorService: " + executorService + " complete."); } return answer; } private void onThreadPoolCreated(ExecutorService executorService, Object source, String threadPoolProfileId) { // add to internal list of thread pools executorServices.add(executorService); String id; String sourceId = null; String routeId = null; // extract id from source if (source instanceof OptionalIdentifiedDefinition) { id = ((OptionalIdentifiedDefinition) source).idOrCreate(camelContext.getNodeIdFactory()); // and let source be the short name of the pattern sourceId = ((OptionalIdentifiedDefinition) source).getShortName(); } else if (source instanceof String) { id = (String) source; } else if (source != null) { // fallback and use the simple class name with hashcode for the id so its unique for this given source id = source.getClass().getSimpleName() + "(" + ObjectHelper.getIdentityHashCode(source) + ")"; } else { // no source, so fallback and use the simple class name from thread pool and its hashcode identity so its unique id = executorService.getClass().getSimpleName() + "(" + ObjectHelper.getIdentityHashCode(executorService) + ")"; } // id is mandatory ObjectHelper.notEmpty(id, "id for thread pool " + executorService); // extract route id if possible if (source instanceof ProcessorDefinition) { RouteDefinition route = ProcessorDefinitionHelper.getRoute((ProcessorDefinition) source); if (route != null) { routeId = route.idOrCreate(camelContext.getNodeIdFactory()); } } // let lifecycle strategy be notified as well which can let it be managed in JMX as well if (executorService instanceof ThreadPoolExecutor) { ThreadPoolExecutor threadPool = (ThreadPoolExecutor) executorService; for (LifecycleStrategy lifecycle : camelContext.getLifecycleStrategies()) { lifecycle.onThreadPoolAdd(camelContext, threadPool, id, sourceId, routeId, threadPoolProfileId); } } // now call strategy to allow custom logic onNewExecutorService(executorService); } /** * Strategy callback when a new {@link java.util.concurrent.ExecutorService} have been created. * * @param executorService the created {@link java.util.concurrent.ExecutorService} */ protected void onNewExecutorService(ExecutorService executorService) { // noop } @Override protected void doStart() throws Exception { if (threadNamePattern == null) { // set default name pattern which includes the camel context name threadNamePattern = "Camel (" + camelContext.getName() + ") thread #${counter} - ${name}"; } } @Override protected void doStop() throws Exception { // noop } @Override protected void doShutdown() throws Exception { // shutdown all executor services for (ExecutorService executorService : executorServices) { // only log if something goes wrong as we want to shutdown them all try { shutdownNow(executorService); } catch (Throwable e) { LOG.warn("Error occurred during shutdown of ExecutorService: " + executorService + ". This exception will be ignored.", e); } } executorServices.clear(); // do not clear the default profile as we could potential be restarted Iterator<ThreadPoolProfile> it = threadPoolProfiles.values().iterator(); while (it.hasNext()) { ThreadPoolProfile profile = it.next(); if (!profile.isDefaultProfile()) { it.remove(); } } } }