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.processor; 018 019import java.io.IOException; 020import java.util.ArrayList; 021import java.util.List; 022import java.util.concurrent.Callable; 023import java.util.concurrent.ExecutorService; 024 025import org.apache.camel.AsyncCallback; 026import org.apache.camel.AsyncProcessor; 027import org.apache.camel.Endpoint; 028import org.apache.camel.EndpointAware; 029import org.apache.camel.Exchange; 030import org.apache.camel.ExchangePattern; 031import org.apache.camel.Expression; 032import org.apache.camel.Message; 033import org.apache.camel.Processor; 034import org.apache.camel.StreamCache; 035import org.apache.camel.Traceable; 036import org.apache.camel.impl.DefaultExchange; 037import org.apache.camel.support.ServiceSupport; 038import org.apache.camel.util.AsyncProcessorHelper; 039import org.apache.camel.util.EventHelper; 040import org.apache.camel.util.ExchangeHelper; 041import org.apache.camel.util.ObjectHelper; 042import org.apache.camel.util.ServiceHelper; 043import org.apache.camel.util.StopWatch; 044import org.slf4j.Logger; 045import org.slf4j.LoggerFactory; 046 047/** 048 * Processor for wire tapping exchanges to an endpoint destination. 049 * 050 * @version 051 */ 052public class WireTapProcessor extends ServiceSupport implements AsyncProcessor, Traceable, EndpointAware { 053 private static final Logger LOG = LoggerFactory.getLogger(WireTapProcessor.class); 054 private final Endpoint destination; 055 private final Processor processor; 056 private final ExchangePattern exchangePattern; 057 private final ExecutorService executorService; 058 private volatile boolean shutdownExecutorService; 059 060 // expression or processor used for populating a new exchange to send 061 // as opposed to traditional wiretap that sends a copy of the original exchange 062 private Expression newExchangeExpression; 063 private List<Processor> newExchangeProcessors; 064 private boolean copy; 065 private Processor onPrepare; 066 067 public WireTapProcessor(Endpoint destination, Processor processor, ExchangePattern exchangePattern, 068 ExecutorService executorService, boolean shutdownExecutorService) { 069 this.destination = destination; 070 this.processor = processor; 071 this.exchangePattern = exchangePattern; 072 ObjectHelper.notNull(executorService, "executorService"); 073 this.executorService = executorService; 074 this.shutdownExecutorService = shutdownExecutorService; 075 } 076 077 @Override 078 public String toString() { 079 return "WireTap[" + destination + "]"; 080 } 081 082 @Override 083 public String getTraceLabel() { 084 return "wireTap(" + destination + ")"; 085 } 086 087 public Endpoint getEndpoint() { 088 return destination; 089 } 090 091 public void process(Exchange exchange) throws Exception { 092 AsyncProcessorHelper.process(this, exchange); 093 } 094 095 public boolean process(final Exchange exchange, final AsyncCallback callback) { 096 if (!isStarted()) { 097 throw new IllegalStateException("WireTapProcessor has not been started: " + this); 098 } 099 100 // must configure the wire tap beforehand 101 Exchange target; 102 try { 103 target = configureExchange(exchange, exchangePattern); 104 } catch (Exception e) { 105 exchange.setException(e); 106 callback.done(true); 107 return true; 108 } 109 110 final Exchange wireTapExchange = target; 111 112 // send the exchange to the destination using an executor service 113 executorService.submit(new Callable<Exchange>() { 114 public Exchange call() throws Exception { 115 final StopWatch watch = new StopWatch(); 116 try { 117 EventHelper.notifyExchangeSending(wireTapExchange.getContext(), wireTapExchange, destination); 118 LOG.debug(">>>> (wiretap) {} {}", destination, wireTapExchange); 119 processor.process(wireTapExchange); 120 } catch (Throwable e) { 121 LOG.warn("Error occurred during processing " + wireTapExchange + " wiretap to " + destination + ". This exception will be ignored.", e); 122 } finally { 123 // emit event that the exchange was sent to the endpoint 124 long timeTaken = watch.stop(); 125 EventHelper.notifyExchangeSent(wireTapExchange.getContext(), wireTapExchange, destination, timeTaken); 126 } 127 return wireTapExchange; 128 } 129 }); 130 131 // continue routing this synchronously 132 callback.done(true); 133 return true; 134 } 135 136 137 protected Exchange configureExchange(Exchange exchange, ExchangePattern pattern) throws IOException { 138 Exchange answer; 139 if (copy) { 140 // use a copy of the original exchange 141 answer = configureCopyExchange(exchange); 142 } else { 143 // use a new exchange 144 answer = configureNewExchange(exchange); 145 } 146 147 // set property which endpoint we send to 148 answer.setProperty(Exchange.TO_ENDPOINT, destination.getEndpointUri()); 149 150 // prepare the exchange 151 if (newExchangeExpression != null) { 152 Object body = newExchangeExpression.evaluate(answer, Object.class); 153 if (body != null) { 154 answer.getIn().setBody(body); 155 } 156 } 157 158 if (newExchangeProcessors != null) { 159 for (Processor processor : newExchangeProcessors) { 160 try { 161 processor.process(answer); 162 } catch (Exception e) { 163 throw ObjectHelper.wrapRuntimeCamelException(e); 164 } 165 } 166 } 167 168 // if the body is a stream cache we must use a copy of the stream in the wire tapped exchange 169 Message msg = answer.hasOut() ? answer.getOut() : answer.getIn(); 170 if (msg.getBody() instanceof StreamCache) { 171 // in parallel processing case, the stream must be copied, therefore get the stream 172 StreamCache cache = (StreamCache) msg.getBody(); 173 StreamCache copied = cache.copy(); 174 if (copied != null) { 175 msg.setBody(copied); 176 } 177 } 178 179 // invoke on prepare on the exchange if specified 180 if (onPrepare != null) { 181 try { 182 onPrepare.process(answer); 183 } catch (Exception e) { 184 throw ObjectHelper.wrapRuntimeCamelException(e); 185 } 186 } 187 188 return answer; 189 } 190 191 private Exchange configureCopyExchange(Exchange exchange) { 192 // must use a copy as we dont want it to cause side effects of the original exchange 193 Exchange copy = ExchangeHelper.createCorrelatedCopy(exchange, false); 194 // set MEP to InOnly as this wire tap is a fire and forget 195 copy.setPattern(ExchangePattern.InOnly); 196 return copy; 197 } 198 199 private Exchange configureNewExchange(Exchange exchange) { 200 return new DefaultExchange(exchange.getFromEndpoint(), ExchangePattern.InOnly); 201 } 202 203 public List<Processor> getNewExchangeProcessors() { 204 return newExchangeProcessors; 205 } 206 207 public void setNewExchangeProcessors(List<Processor> newExchangeProcessors) { 208 this.newExchangeProcessors = newExchangeProcessors; 209 } 210 211 public Expression getNewExchangeExpression() { 212 return newExchangeExpression; 213 } 214 215 public void setNewExchangeExpression(Expression newExchangeExpression) { 216 this.newExchangeExpression = newExchangeExpression; 217 } 218 219 public void addNewExchangeProcessor(Processor processor) { 220 if (newExchangeProcessors == null) { 221 newExchangeProcessors = new ArrayList<Processor>(); 222 } 223 newExchangeProcessors.add(processor); 224 } 225 226 public boolean isCopy() { 227 return copy; 228 } 229 230 public void setCopy(boolean copy) { 231 this.copy = copy; 232 } 233 234 public Processor getOnPrepare() { 235 return onPrepare; 236 } 237 238 public void setOnPrepare(Processor onPrepare) { 239 this.onPrepare = onPrepare; 240 } 241 242 @Override 243 protected void doStart() throws Exception { 244 ServiceHelper.startService(processor); 245 } 246 247 @Override 248 protected void doStop() throws Exception { 249 ServiceHelper.stopService(processor); 250 } 251 252 @Override 253 protected void doShutdown() throws Exception { 254 ServiceHelper.stopAndShutdownService(processor); 255 if (shutdownExecutorService) { 256 destination.getCamelContext().getExecutorServiceManager().shutdownNow(executorService); 257 } 258 } 259}