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     */
017    package org.apache.camel.processor;
018    
019    import java.util.concurrent.Callable;
020    import java.util.concurrent.ExecutorService;
021    
022    import org.apache.camel.Endpoint;
023    import org.apache.camel.Exchange;
024    import org.apache.camel.ExchangePattern;
025    import org.apache.camel.Expression;
026    import org.apache.camel.Processor;
027    import org.apache.camel.Producer;
028    import org.apache.camel.ProducerCallback;
029    import org.apache.camel.impl.DefaultExchange;
030    import org.apache.camel.util.ExchangeHelper;
031    import org.apache.camel.util.ObjectHelper;
032    import org.apache.camel.util.concurrent.ExecutorServiceHelper;
033    
034    /**
035     * Processor for wire tapping exchanges to an endpoint destination.
036     *
037     * @version $Revision: 885197 $
038     */
039    public class WireTapProcessor extends SendProcessor {
040    
041        private static final int DEFAULT_THREADPOOL_SIZE = 10;
042        private ExecutorService executorService;
043    
044        // expression or processor used for populating a new exchange to send
045        // as opposed to traditional wiretap that sends a copy of the original exchange
046        private Expression newExchangeExpression;
047        private Processor newExchangeProcessor;
048    
049        public WireTapProcessor(Endpoint destination) {
050            super(destination);
051        }
052    
053        public WireTapProcessor(Endpoint destination, ExchangePattern pattern) {
054            super(destination, pattern);
055        }
056    
057        @Override
058        protected void doStart() throws Exception {
059            super.doStart();
060        }
061    
062        @Override
063        protected void doStop() throws Exception {
064            if (executorService != null) {
065                executorService.shutdown();
066                // must null it so we can restart
067                executorService = null;
068            }
069            super.doStop();
070        }
071    
072        @Override
073        public String toString() {
074            return "WireTap[" + destination.getEndpointUri() + "]";
075        }
076    
077        @Override
078        public String getTraceLabel() {
079            return "wireTap(" + destination.getEndpointUri() + ")";
080        }
081    
082        public void process(Exchange exchange) throws Exception {
083            getProducerCache(exchange).doInProducer(destination, exchange, pattern, new ProducerCallback<Exchange>() {
084                public Exchange doInProducer(Producer producer, Exchange exchange, ExchangePattern pattern) throws Exception {
085                    Exchange wireTapExchange = configureExchange(exchange, pattern);
086                    procesWireTap(producer, wireTapExchange);
087                    return wireTapExchange;
088                }
089            });
090        }
091    
092        /**
093         * Wiretaps the exchange.
094         *
095         * @param exchange  the exchange to wire tap
096         */
097        protected void procesWireTap(final Producer producer, final Exchange exchange) {
098            // use submit instead of execute to force it to use a new thread, execute might
099            // decide to use current thread, so we must submit a new task
100            // as we dont care for the response we dont hold the future object and wait for the result
101            getExecutorService().submit(new Callable<Exchange>() {
102                public Exchange call() throws Exception {
103                    if (LOG.isDebugEnabled()) {
104                        LOG.debug("Processing wiretap: " + exchange);
105                    }
106                    producer.process(exchange);
107                    return exchange;
108                }
109            });
110        }
111    
112        @Override
113        protected Exchange configureExchange(Exchange exchange, ExchangePattern pattern) {
114            Exchange answer;
115            if (newExchangeProcessor == null && newExchangeExpression == null) {
116                // use a copy of the original exchange
117                answer = configureCopyExchange(exchange);
118            } else {
119                // use a new exchange
120                answer = configureNewExchange(exchange);
121            }
122            // set property which endpoint we send to
123            answer.setProperty(Exchange.TO_ENDPOINT, destination.getEndpointUri());
124            return answer;
125        }
126    
127        private Exchange configureCopyExchange(Exchange exchange) {
128            // must use a copy as we dont want it to cause side effects of the original exchange
129            Exchange copy = ExchangeHelper.createCorrelatedCopy(exchange, false);
130            // set MEP to InOnly as this wire tap is a fire and forget
131            copy.setPattern(ExchangePattern.InOnly);
132            return copy;
133        }
134    
135        private Exchange configureNewExchange(Exchange exchange) {
136            Exchange answer = new DefaultExchange(exchange.getContext(), ExchangePattern.InOnly);
137            // use destination os origin of this new exchange
138            answer.setFromEndpoint(getDestination());
139    
140            // prepare the exchange
141            if (newExchangeProcessor != null) {
142                try {
143                    newExchangeProcessor.process(answer);
144                } catch (Exception e) {
145                    throw ObjectHelper.wrapRuntimeCamelException(e);
146                }
147            } else {
148                Object body = newExchangeExpression.evaluate(answer, Object.class);
149                if (body != null) {
150                    answer.getIn().setBody(body);
151                }
152            }
153    
154            return answer;
155        }
156    
157        public ExecutorService getExecutorService() {
158            if (executorService == null || executorService.isShutdown()) {
159                executorService = createExecutorService();
160            }
161            return executorService;
162        }
163    
164        private ExecutorService createExecutorService() {
165            return ExecutorServiceHelper.newScheduledThreadPool(DEFAULT_THREADPOOL_SIZE, this.toString(), true);
166        }
167    
168        public void setExecutorService(ExecutorService executorService) {
169            this.executorService = executorService;
170        }
171    
172        public Processor getNewExchangeProcessor() {
173            return newExchangeProcessor;
174        }
175    
176        public void setNewExchangeProcessor(Processor newExchangeProcessor) {
177            this.newExchangeProcessor = newExchangeProcessor;
178        }
179    
180        public Expression getNewExchangeExpression() {
181            return newExchangeExpression;
182        }
183    
184        public void setNewExchangeExpression(Expression newExchangeExpression) {
185            this.newExchangeExpression = newExchangeExpression;
186        }
187    }