001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.camel.util.concurrent; 018 019import java.util.Collection; 020import java.util.List; 021import java.util.concurrent.Callable; 022import java.util.concurrent.ExecutionException; 023import java.util.concurrent.Future; 024import java.util.concurrent.RejectedExecutionException; 025import java.util.concurrent.RejectedExecutionHandler; 026import java.util.concurrent.ScheduledExecutorService; 027import java.util.concurrent.ScheduledFuture; 028import java.util.concurrent.ScheduledThreadPoolExecutor; 029import java.util.concurrent.ThreadFactory; 030import java.util.concurrent.TimeUnit; 031import java.util.concurrent.TimeoutException; 032 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036/** 037 * A sized {@link ScheduledExecutorService} which will reject executing tasks if the task queue is full. 038 * <p/> 039 * The {@link ScheduledThreadPoolExecutor} which is the default implementation of the {@link ScheduledExecutorService} 040 * has unbounded task queue, which mean you can keep scheduling tasks which may cause the system to run out of memory. 041 * <p/> 042 * This class is a wrapped for {@link ScheduledThreadPoolExecutor} to reject executing tasks if an upper limit of the 043 * task queue has been reached. 044 */ 045public class SizedScheduledExecutorService implements ScheduledExecutorService { 046 047 private static final Logger LOG = LoggerFactory.getLogger(SizedScheduledExecutorService.class); 048 public static final String QUEUE_SIZE_LIMIT_REACHED = "Task rejected due queue size limit reached"; 049 private final ScheduledThreadPoolExecutor delegate; 050 private final long queueSize; 051 052 /** 053 * Creates a new sized {@link ScheduledExecutorService} with the given queue size as upper task limit. 054 * 055 * @param delegate the delegate of the actual thread pool implementation 056 * @param queueSize the upper queue size, use 0 or negative value for unlimited 057 */ 058 public SizedScheduledExecutorService(ScheduledThreadPoolExecutor delegate, long queueSize) { 059 this.delegate = delegate; 060 this.queueSize = queueSize; 061 } 062 063 /** 064 * Gets the wrapped {@link ScheduledThreadPoolExecutor} 065 */ 066 public ScheduledThreadPoolExecutor getScheduledThreadPoolExecutor() { 067 return delegate; 068 } 069 070 @Override 071 public <V> ScheduledFuture<V> schedule(Callable<V> task, long delay, TimeUnit timeUnit) { 072 if (canScheduleOrExecute()) { 073 return delegate.schedule(task, delay, timeUnit); 074 } else { 075 throw new RejectedExecutionException(QUEUE_SIZE_LIMIT_REACHED); 076 } 077 } 078 079 @Override 080 public ScheduledFuture<?> schedule(Runnable task, long delay, TimeUnit timeUnit) { 081 if (canScheduleOrExecute()) { 082 return delegate.schedule(task, delay, timeUnit); 083 } else { 084 throw new RejectedExecutionException(QUEUE_SIZE_LIMIT_REACHED); 085 } 086 } 087 088 @Override 089 public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit timeUnit) { 090 if (canScheduleOrExecute()) { 091 return delegate.scheduleAtFixedRate(task, initialDelay, period, timeUnit); 092 } else { 093 throw new RejectedExecutionException(QUEUE_SIZE_LIMIT_REACHED); 094 } 095 } 096 097 @Override 098 public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long initialDelay, long period, TimeUnit timeUnit) { 099 if (canScheduleOrExecute()) { 100 return delegate.scheduleWithFixedDelay(task, initialDelay, period, timeUnit); 101 } else { 102 throw new RejectedExecutionException(QUEUE_SIZE_LIMIT_REACHED); 103 } 104 } 105 106 @Override 107 public boolean awaitTermination(long timeout, TimeUnit timeUnit) throws InterruptedException { 108 return delegate.awaitTermination(timeout, timeUnit); 109 } 110 111 public int getActiveCount() { 112 return delegate.getActiveCount(); 113 } 114 115 public long getCompletedTaskCount() { 116 return delegate.getCompletedTaskCount(); 117 } 118 119 public int getCorePoolSize() { 120 return delegate.getCorePoolSize(); 121 } 122 123 public long getKeepAliveTime(TimeUnit timeUnit) { 124 return delegate.getKeepAliveTime(timeUnit); 125 } 126 127 public int getLargestPoolSize() { 128 return delegate.getLargestPoolSize(); 129 } 130 131 public int getMaximumPoolSize() { 132 return delegate.getMaximumPoolSize(); 133 } 134 135 public int getPoolSize() { 136 return delegate.getPoolSize(); 137 } 138 139 public RejectedExecutionHandler getRejectedExecutionHandler() { 140 return delegate.getRejectedExecutionHandler(); 141 } 142 143 public long getTaskCount() { 144 return delegate.getTaskCount(); 145 } 146 147 public ThreadFactory getThreadFactory() { 148 return delegate.getThreadFactory(); 149 } 150 151 @Override 152 public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException { 153 if (canScheduleOrExecute()) { 154 return delegate.invokeAll(tasks); 155 } else { 156 throw new RejectedExecutionException(QUEUE_SIZE_LIMIT_REACHED); 157 } 158 } 159 160 @Override 161 public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit timeUnit) 162 throws InterruptedException { 163 if (canScheduleOrExecute()) { 164 return delegate.invokeAll(tasks, timeout, timeUnit); 165 } else { 166 throw new RejectedExecutionException(QUEUE_SIZE_LIMIT_REACHED); 167 } 168 } 169 170 @Override 171 public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException { 172 if (canScheduleOrExecute()) { 173 return delegate.invokeAny(tasks); 174 } else { 175 throw new RejectedExecutionException(QUEUE_SIZE_LIMIT_REACHED); 176 } 177 } 178 179 @Override 180 public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit timeUnit) 181 throws InterruptedException, ExecutionException, TimeoutException { 182 if (canScheduleOrExecute()) { 183 return delegate.invokeAny(tasks, timeout, timeUnit); 184 } else { 185 throw new RejectedExecutionException(QUEUE_SIZE_LIMIT_REACHED); 186 } 187 } 188 189 @Override 190 public boolean isShutdown() { 191 return delegate.isShutdown(); 192 } 193 194 @Override 195 public boolean isTerminated() { 196 return delegate.isTerminated(); 197 } 198 199 public boolean isTerminating() { 200 return delegate.isTerminating(); 201 } 202 203 public int prestartAllCoreThreads() { 204 return delegate.prestartAllCoreThreads(); 205 } 206 207 public boolean prestartCoreThread() { 208 return delegate.prestartCoreThread(); 209 } 210 211 public void purge() { 212 delegate.purge(); 213 } 214 215 public void setCorePoolSize(int corePoolSize) { 216 delegate.setCorePoolSize(corePoolSize); 217 } 218 219 public void setKeepAliveTime(long keepAliveTime, TimeUnit timeUnit) { 220 delegate.setKeepAliveTime(keepAliveTime, timeUnit); 221 } 222 223 public void setMaximumPoolSize(int maximumPoolSize) { 224 delegate.setMaximumPoolSize(maximumPoolSize); 225 } 226 227 public void setRejectedExecutionHandler(RejectedExecutionHandler rejectedExecutionHandler) { 228 delegate.setRejectedExecutionHandler(rejectedExecutionHandler); 229 } 230 231 public void setThreadFactory(ThreadFactory threadFactory) { 232 delegate.setThreadFactory(threadFactory); 233 } 234 235 @Override 236 public void shutdown() { 237 delegate.shutdown(); 238 } 239 240 @Override 241 public List<Runnable> shutdownNow() { 242 return delegate.shutdownNow(); 243 } 244 245 @Override 246 public <T> Future<T> submit(Callable<T> task) { 247 if (canScheduleOrExecute()) { 248 return delegate.submit(task); 249 } else { 250 throw new RejectedExecutionException(QUEUE_SIZE_LIMIT_REACHED); 251 } 252 } 253 254 @Override 255 public Future<?> submit(Runnable task) { 256 if (canScheduleOrExecute()) { 257 return delegate.submit(task); 258 } else { 259 throw new RejectedExecutionException(QUEUE_SIZE_LIMIT_REACHED); 260 } 261 } 262 263 @Override 264 public <T> Future<T> submit(Runnable task, T result) { 265 if (canScheduleOrExecute()) { 266 return delegate.submit(task, result); 267 } else { 268 throw new RejectedExecutionException(QUEUE_SIZE_LIMIT_REACHED); 269 } 270 } 271 272 @Override 273 public void execute(Runnable task) { 274 if (canScheduleOrExecute()) { 275 delegate.execute(task); 276 } else { 277 throw new RejectedExecutionException(QUEUE_SIZE_LIMIT_REACHED); 278 } 279 } 280 281 public void allowCoreThreadTimeOut(boolean value) { 282 delegate.allowCoreThreadTimeOut(value); 283 } 284 285 public boolean allowsCoreThreadTimeOut() { 286 return delegate.allowsCoreThreadTimeOut(); 287 } 288 289 /** 290 * Can the task be scheduled or executed? 291 * 292 * @return <tt>true</tt> to accept, <tt>false</tt> to not accept 293 */ 294 protected boolean canScheduleOrExecute() { 295 if (queueSize <= 0) { 296 return true; 297 } 298 299 int size = delegate.getQueue().size(); 300 boolean answer = size < queueSize; 301 if (LOG.isTraceEnabled()) { 302 LOG.trace("canScheduleOrExecute {} < {} -> {}", size, queueSize, answer); 303 } 304 return answer; 305 } 306 307 @Override 308 public String toString() { 309 // the thread factory often have more precise details what the thread pool is used for 310 if (delegate.getThreadFactory() instanceof CamelThreadFactory camelThreadFactory) { 311 String name = camelThreadFactory.getName(); 312 return super.toString() + "[" + name + "]"; 313 } else { 314 return super.toString(); 315 } 316 } 317}