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

org.apache.camel.impl.engine.DefaultProducerCache Maven / Gradle / Ivy

There is a newer version: 4.9.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.impl.engine;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.RejectedExecutionException;

import org.apache.camel.AsyncCallback;
import org.apache.camel.AsyncProcessor;
import org.apache.camel.AsyncProducer;
import org.apache.camel.CamelContext;
import org.apache.camel.Endpoint;
import org.apache.camel.Exchange;
import org.apache.camel.ExchangePattern;
import org.apache.camel.ExtendedCamelContext;
import org.apache.camel.FailedToCreateProducerException;
import org.apache.camel.Processor;
import org.apache.camel.Producer;
import org.apache.camel.StatefulService;
import org.apache.camel.processor.CamelInternalProcessor;
import org.apache.camel.processor.SharedCamelInternalProcessor;
import org.apache.camel.spi.EndpointUtilizationStatistics;
import org.apache.camel.spi.ProducerCache;
import org.apache.camel.support.CamelContextHelper;
import org.apache.camel.support.EventHelper;
import org.apache.camel.support.service.ServiceHelper;
import org.apache.camel.support.service.ServiceSupport;
import org.apache.camel.util.StopWatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Default implementation of {@link ProducerCache}.
 */
public class DefaultProducerCache extends ServiceSupport implements ProducerCache {

    private static final Logger LOG = LoggerFactory.getLogger(DefaultProducerCache.class);
    private static final long ACQUIRE_WAIT_TIME = 30000;

    private final ExtendedCamelContext camelContext;
    private final ProducerServicePool producers;
    private final Object source;
    private final SharedCamelInternalProcessor internalProcessor;

    private EndpointUtilizationStatistics statistics;
    private boolean eventNotifierEnabled = true;
    private boolean extendedStatistics;
    private int maxCacheSize;

    public DefaultProducerCache(Object source, CamelContext camelContext, int cacheSize) {
        this.source = source;
        this.camelContext = (ExtendedCamelContext) camelContext;
        this.maxCacheSize = cacheSize <= 0 ? CamelContextHelper.getMaximumCachePoolSize(camelContext) : cacheSize;
        if (cacheSize >= 0) {
            this.producers = createServicePool(camelContext, maxCacheSize);
        } else {
            // no cache then empty
            this.producers = null;
        }

        // only if JMX is enabled
        if (camelContext.getManagementStrategy() != null && camelContext.getManagementStrategy().getManagementAgent() != null) {
            this.extendedStatistics = camelContext.getManagementStrategy().getManagementAgent().getStatisticsLevel().isExtended();
        } else {
            this.extendedStatistics = false;
        }

        // internal processor used for sending
        internalProcessor = new SharedCamelInternalProcessor(camelContext, new CamelInternalProcessor.UnitOfWorkProcessorAdvice(null, camelContext));
    }

    protected ProducerServicePool createServicePool(CamelContext camelContext, int cacheSize) {
        return new ProducerServicePool(Endpoint::createAsyncProducer, Producer::getEndpoint, cacheSize);
    }

    @Override
    public boolean isEventNotifierEnabled() {
        return eventNotifierEnabled;
    }

    @Override
    public void setEventNotifierEnabled(boolean eventNotifierEnabled) {
        this.eventNotifierEnabled = eventNotifierEnabled;
    }

    public boolean isExtendedStatistics() {
        return extendedStatistics;
    }

    /**
     * Whether extended JMX statistics is enabled for {@link org.apache.camel.spi.EndpointUtilizationStatistics}
     */
    public void setExtendedStatistics(boolean extendedStatistics) {
        this.extendedStatistics = extendedStatistics;
    }

    public CamelContext getCamelContext() {
        return camelContext;
    }

    @Override
    public Object getSource() {
        return source;
    }

    @Override
    public AsyncProducer acquireProducer(Endpoint endpoint) {
        try {
            AsyncProducer producer = producers.acquire(endpoint);
            if (statistics != null) {
                statistics.onHit(endpoint.getEndpointUri());
            }
            // if producer is starting then wait for it to be ready
            if (producer instanceof StatefulService) {
                StatefulService ss = (StatefulService) producer;
                if (ss.isStarting()) {
                    LOG.trace("Waiting for producer to finish starting: {}", producer);
                    StopWatch watch = new StopWatch();
                    boolean done = false;
                    while (!done) {
                        done = !ss.isStarting() || watch.taken() > ACQUIRE_WAIT_TIME;
                        if (!done) {
                            Thread.sleep(5);
                            if (LOG.isTraceEnabled()) {
                                LOG.trace("Waiting {} ms for producer to finish starting: {} state: {}", watch.taken(), producer, ss.getStatus());
                            }
                        }
                    }
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Waited {} ms for producer to finish starting: {} state: {}", watch.taken(), producer, ss.getStatus());
                    }
                }
            }
            return producer;
        } catch (Throwable e) {
            throw new FailedToCreateProducerException(endpoint, e);
        }
    }

    @Override
    public void releaseProducer(Endpoint endpoint, AsyncProducer producer) {
        producers.release(endpoint, producer);
    }

    @Override
    public Exchange send(Endpoint endpoint, Exchange exchange, Processor resultProcessor) {
        if (camelContext.isStopped()) {
            exchange.setException(new RejectedExecutionException("CamelContext is stopped"));
            return exchange;
        }

        AsyncProducer producer = acquireProducer(endpoint);
        try {
            // now lets dispatch
            LOG.debug(">>>> {} {}", endpoint, exchange);

            // set property which endpoint we send to
            exchange.setProperty(Exchange.TO_ENDPOINT, endpoint.getEndpointUri());

            // send the exchange using the processor
            StopWatch watch = null;
            try {
                if (eventNotifierEnabled && camelContext.isEventNotificationApplicable()) {
                    boolean sending = EventHelper.notifyExchangeSending(exchange.getContext(), exchange, endpoint);
                    if (sending) {
                        watch = new StopWatch();
                    }
                }

                // invoke the synchronous method
                internalProcessor.process(exchange, producer, resultProcessor);

            } catch (Throwable e) {
                // ensure exceptions is caught and set on the exchange
                exchange.setException(e);
            } finally {
                // emit event that the exchange was sent to the endpoint
                if (watch != null) {
                    long timeTaken = watch.taken();
                    EventHelper.notifyExchangeSent(exchange.getContext(), exchange, endpoint, timeTaken);
                }
            }
            return exchange;
        } finally {
            releaseProducer(endpoint, producer);
        }
    }

    /**
     * Asynchronously sends an exchange to an endpoint using a supplied
     * {@link Processor} to populate the exchange
     * 

* This method will neither throw an exception nor complete future exceptionally. * If processing of the given Exchange failed then the exception is stored on the return Exchange * * @param endpoint the endpoint to send the exchange to * @param pattern the message {@link ExchangePattern} such as * {@link ExchangePattern#InOnly} or {@link ExchangePattern#InOut} * @param processor the transformer used to populate the new exchange * @param resultProcessor a processor to process the exchange when the send is complete. * @param future the preexisting future to complete when processing is done or null if to create new one * @return future that completes with exchange when processing is done. Either passed into future parameter * or new one if parameter was null */ @Deprecated public CompletableFuture asyncSend(Endpoint endpoint, ExchangePattern pattern, Processor processor, Processor resultProcessor, CompletableFuture future) { return asyncSendExchange(endpoint, pattern, processor, resultProcessor, null, future); } @Override public CompletableFuture asyncSendExchange(Endpoint endpoint, ExchangePattern pattern, Processor processor, Processor resultProcessor, Exchange exchange, CompletableFuture future) { if (exchange == null) { exchange = pattern != null ? endpoint.createExchange(pattern) : endpoint.createExchange(); } return doAsyncSendExchange(endpoint, processor, resultProcessor, exchange, future); } protected CompletableFuture doAsyncSendExchange(Endpoint endpoint, Processor processor, Processor resultProcessor, Exchange exchange, CompletableFuture f) { CompletableFuture future = f != null ? f : new CompletableFuture<>(); AsyncProducerCallback cb = (p, e, c) -> asyncDispatchExchange(endpoint, p, resultProcessor, e, c); try { if (processor instanceof AsyncProcessor) { ((AsyncProcessor) processor).process(exchange, doneSync -> doInAsyncProducer(endpoint, exchange, ds -> future.complete(exchange), cb)); } else { if (processor != null) { processor.process(exchange); } doInAsyncProducer(endpoint, exchange, ds -> future.complete(exchange), cb); } } catch (Throwable e) { // populate failed so return exchange.setException(e); future.complete(exchange); } return future; } @Override public boolean doInAsyncProducer(Endpoint endpoint, Exchange exchange, AsyncCallback callback, AsyncProducerCallback producerCallback) { AsyncProducer producer; try { // get the producer and we do not mind if its pooled as we can handle returning it back to the pool producer = acquireProducer(endpoint); if (producer == null) { if (isStopped()) { LOG.warn("Ignoring exchange sent after processor is stopped: {}", exchange); callback.done(true); return true; } else { exchange.setException(new IllegalStateException("No producer, this processor has not been started: " + this)); callback.done(true); return true; } } } catch (Throwable e) { exchange.setException(e); callback.done(true); return true; } try { // record timing for sending the exchange using the producer StopWatch watch; if (eventNotifierEnabled && camelContext.isEventNotificationApplicable()) { boolean sending = EventHelper.notifyExchangeSending(exchange.getContext(), exchange, endpoint); if (sending) { watch = new StopWatch(); } else { watch = null; } } else { watch = null; } // invoke the callback return producerCallback.doInAsyncProducer(producer, exchange, doneSync -> { try { if (watch != null) { long timeTaken = watch.taken(); // emit event that the exchange was sent to the endpoint EventHelper.notifyExchangeSent(exchange.getContext(), exchange, endpoint, timeTaken); } // release back to the pool releaseProducer(endpoint, producer); } finally { callback.done(doneSync); } }); } catch (Throwable e) { // ensure exceptions is caught and set on the exchange if (exchange != null) { exchange.setException(e); } callback.done(true); return true; } } protected boolean asyncDispatchExchange(Endpoint endpoint, AsyncProducer producer, Processor resultProcessor, Exchange exchange, AsyncCallback callback) { // now lets dispatch LOG.debug(">>>> {} {}", endpoint, exchange); // set property which endpoint we send to exchange.setProperty(Exchange.TO_ENDPOINT, endpoint.getEndpointUri()); // send the exchange using the processor try { if (eventNotifierEnabled && camelContext.isEventNotificationApplicable()) { callback = new EventNotifierCallback(callback, exchange, endpoint); } // invoke the asynchronous method return internalProcessor.process(exchange, callback, producer, resultProcessor); } catch (Throwable e) { // ensure exceptions is caught and set on the exchange exchange.setException(e); callback.done(true); return true; } } @Override protected void doInit() throws Exception { if (extendedStatistics) { int max = maxCacheSize == 0 ? CamelContextHelper.getMaximumCachePoolSize(camelContext) : maxCacheSize; statistics = new DefaultEndpointUtilizationStatistics(max); } ServiceHelper.initService(producers); } @Override protected void doStart() throws Exception { if (statistics != null) { statistics.clear(); } ServiceHelper.startService(producers); } @Override protected void doStop() throws Exception { ServiceHelper.stopService(producers); } @Override protected void doShutdown() throws Exception { ServiceHelper.stopAndShutdownServices(producers); } @Override public int size() { int size = producers != null ? producers.size() : 0; LOG.trace("size = {}", size); return size; } @Override public int getCapacity() { return maxCacheSize; } @Override public synchronized void purge() { try { if (producers != null) { producers.stop(); producers.start(); } } catch (Exception e) { LOG.debug("Error restarting producers", e); } if (statistics != null) { statistics.clear(); } } @Override public void cleanUp() { if (producers != null) { producers.cleanUp(); } } @Override public EndpointUtilizationStatistics getEndpointUtilizationStatistics() { return statistics; } @Override public String toString() { return "ProducerCache for source: " + source + ", capacity: " + getCapacity(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy