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

org.apache.camel.processor.DelayProcessorSupport Maven / Gradle / Ivy

There is a newer version: 4.6.0
Show newest version
/**
 * 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.processor;

import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.camel.AsyncCallback;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.util.ObjectHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A useful base class for any processor which provides some kind of throttling
 * or delayed processing.
 * 

* This implementation will block while waiting. * * @version */ public abstract class DelayProcessorSupport extends DelegateAsyncProcessor { protected final Logger log = LoggerFactory.getLogger(getClass()); private final CamelContext camelContext; private final ScheduledExecutorService executorService; private final boolean shutdownExecutorService; private boolean asyncDelayed; private boolean callerRunsWhenRejected = true; private final AtomicInteger delayedCount = new AtomicInteger(0); // TODO: Add option to cancel tasks on shutdown so we can stop fast private final class ProcessCall implements Runnable { private final Exchange exchange; private final AsyncCallback callback; ProcessCall(Exchange exchange, AsyncCallback callback) { this.exchange = exchange; this.callback = callback; } public void run() { // we are running now so decrement the counter delayedCount.decrementAndGet(); log.trace("Delayed task woke up and continues routing for exchangeId: {}", exchange.getExchangeId()); if (!isRunAllowed()) { exchange.setException(new RejectedExecutionException("Run is not allowed")); } // process the exchange now that we woke up DelayProcessorSupport.this.processor.process(exchange, new AsyncCallback() { @Override public void done(boolean doneSync) { log.trace("Delayed task done for exchangeId: {}", exchange.getExchangeId()); // we must done the callback from this async callback as well, to ensure callback is done correctly // must invoke done on callback with false, as that is what the original caller would // expect as we returned false in the process method callback.done(false); } }); } } public DelayProcessorSupport(CamelContext camelContext, Processor processor) { this(camelContext, processor, null, false); } public DelayProcessorSupport(CamelContext camelContext, Processor processor, ScheduledExecutorService executorService, boolean shutdownExecutorService) { super(processor); this.camelContext = camelContext; this.executorService = executorService; this.shutdownExecutorService = shutdownExecutorService; } protected boolean processDelay(Exchange exchange, AsyncCallback callback, long delay) { if (!isAsyncDelayed() || exchange.isTransacted()) { // use synchronous delay (also required if using transactions) try { delay(delay, exchange); // then continue routing return processor.process(exchange, callback); } catch (Exception e) { // exception occurred so we are done exchange.setException(e); callback.done(true); return true; } } else { // asynchronous delay so schedule a process call task // and increment the counter (we decrement the counter when we run the ProcessCall) delayedCount.incrementAndGet(); ProcessCall call = new ProcessCall(exchange, callback); try { log.trace("Scheduling delayed task to run in {} millis for exchangeId: {}", delay, exchange.getExchangeId()); executorService.schedule(call, delay, TimeUnit.MILLISECONDS); // tell Camel routing engine we continue routing asynchronous return false; } catch (RejectedExecutionException e) { // we were not allowed to run the ProcessCall, so need to decrement the counter here delayedCount.decrementAndGet(); if (isCallerRunsWhenRejected()) { if (!isRunAllowed()) { exchange.setException(new RejectedExecutionException()); } else { log.debug("Scheduling rejected task, so letting caller run, delaying at first for {} millis for exchangeId: {}", delay, exchange.getExchangeId()); // let caller run by processing try { delay(delay, exchange); } catch (InterruptedException ie) { exchange.setException(ie); } // then continue routing return processor.process(exchange, callback); } } else { exchange.setException(e); } // caller don't run the task so we are done callback.done(true); return true; } } } @Override public boolean process(Exchange exchange, AsyncCallback callback) { if (!isRunAllowed()) { exchange.setException(new RejectedExecutionException("Run is not allowed")); callback.done(true); return true; } // calculate delay and wait long delay; try { delay = calculateDelay(exchange); if (delay <= 0) { // no delay then continue routing log.trace("No delay for exchangeId: {}", exchange.getExchangeId()); return processor.process(exchange, callback); } } catch (Throwable e) { exchange.setException(e); callback.done(true); return true; } return processDelay(exchange, callback, delay); } public boolean isAsyncDelayed() { return asyncDelayed; } public void setAsyncDelayed(boolean asyncDelayed) { this.asyncDelayed = asyncDelayed; } public boolean isCallerRunsWhenRejected() { return callerRunsWhenRejected; } public void setCallerRunsWhenRejected(boolean callerRunsWhenRejected) { this.callerRunsWhenRejected = callerRunsWhenRejected; } protected abstract long calculateDelay(Exchange exchange); /** * Gets the current number of {@link Exchange}s being delayed (hold back due throttle limit hit) */ public int getDelayedCount() { return delayedCount.get(); } /** * Delays the given time before continuing. *

* This implementation will block while waiting * * @param delay the delay time in millis * @param exchange the exchange being processed */ protected void delay(long delay, Exchange exchange) throws InterruptedException { // only run is we are started if (!isRunAllowed()) { return; } if (delay < 0) { return; } else { try { // keep track on delayer counter while we sleep delayedCount.incrementAndGet(); sleep(delay); } catch (InterruptedException e) { handleSleepInterruptedException(e, exchange); } finally { delayedCount.decrementAndGet(); } } } /** * Called when a sleep is interrupted; allows derived classes to handle this case differently */ protected void handleSleepInterruptedException(InterruptedException e, Exchange exchange) throws InterruptedException { if (log.isDebugEnabled()) { log.debug("Sleep interrupted, are we stopping? {}", isStopping() || isStopped()); } Thread.currentThread().interrupt(); throw e; } protected long currentSystemTime() { return System.currentTimeMillis(); } private void sleep(long delay) throws InterruptedException { if (delay <= 0) { return; } log.trace("Sleeping for: {} millis", delay); Thread.sleep(delay); } @Override protected void doStart() throws Exception { if (isAsyncDelayed()) { ObjectHelper.notNull(executorService, "executorService", this); } super.doStart(); } @Override protected void doShutdown() throws Exception { if (shutdownExecutorService && executorService != null) { camelContext.getExecutorServiceManager().shutdownNow(executorService); } super.doShutdown(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy