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.management;
018
019import org.apache.camel.AsyncCallback;
020import org.apache.camel.Exchange;
021import org.apache.camel.Ordered;
022import org.apache.camel.Processor;
023import org.apache.camel.api.management.PerformanceCounter;
024import org.apache.camel.management.mbean.ManagedPerformanceCounter;
025import org.apache.camel.processor.CamelInternalProcessorAdvice;
026import org.apache.camel.processor.DelegateAsyncProcessor;
027import org.apache.camel.util.StopWatch;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030
031/**
032 * JMX enabled processor or advice that uses the {@link org.apache.camel.management.mbean.ManagedCounter} for instrumenting
033 * processing of exchanges.
034 * <p/>
035 * This implementation has been optimised to work in dual mode, either as an advice or as a processor.
036 * The former is faster and the latter is required when the error handler has been configured with redelivery enabled.
037 *
038 * @version 
039 */
040public class InstrumentationProcessor extends DelegateAsyncProcessor implements CamelInternalProcessorAdvice<StopWatch>, Ordered {
041
042    private static final Logger LOG = LoggerFactory.getLogger(InstrumentationProcessor.class);
043    private PerformanceCounter counter;
044    private String type;
045
046    public InstrumentationProcessor(String type, Processor processor) {
047        super(processor);
048        this.type = type;
049    }
050
051    public void setCounter(Object counter) {
052        ManagedPerformanceCounter mpc = null;
053        if (counter instanceof ManagedPerformanceCounter) {
054            mpc = (ManagedPerformanceCounter) counter;
055        }
056
057        if (this.counter instanceof DelegatePerformanceCounter) {
058            ((DelegatePerformanceCounter) this.counter).setCounter(mpc);
059        } else if (mpc != null) {
060            this.counter = mpc;
061        } else if (counter instanceof PerformanceCounter) {
062            this.counter = (PerformanceCounter) counter;
063        }
064    }
065
066    @Override
067    public boolean process(final Exchange exchange, final AsyncCallback callback) {
068        // only record time if stats is enabled
069        final StopWatch watch = (counter != null && counter.isStatisticsEnabled()) ? new StopWatch() : null;
070
071        // mark beginning to process the exchange
072        if (watch != null) {
073            beginTime(exchange);
074        }
075
076        return processor.process(exchange, new AsyncCallback() {
077            public void done(boolean doneSync) {
078                try {
079                    // record end time
080                    if (watch != null) {
081                        recordTime(exchange, watch.taken());
082                    }
083                } finally {
084                    // and let the original callback know we are done as well
085                    callback.done(doneSync);
086                }
087            }
088
089            @Override
090            public String toString() {
091                return InstrumentationProcessor.this.toString();
092            }
093        });
094    }
095
096    protected void beginTime(Exchange exchange) {
097        counter.processExchange(exchange);
098    }
099
100    protected void recordTime(Exchange exchange, long duration) {
101        if (LOG.isTraceEnabled()) {
102            LOG.trace("{}Recording duration: {} millis for exchange: {}", new Object[]{type != null ? type + ": " : "", duration, exchange});
103        }
104
105        if (!exchange.isFailed() && exchange.getException() == null) {
106            counter.completedExchange(exchange, duration);
107        } else {
108            counter.failedExchange(exchange);
109        }
110    }
111
112    public String getType() {
113        return type;
114    }
115
116    public void setType(String type) {
117        this.type = type;
118    }
119
120    @Override
121    public StopWatch before(Exchange exchange) throws Exception {
122        // only record time if stats is enabled
123        StopWatch answer = counter != null && counter.isStatisticsEnabled() ? new StopWatch() : null;
124        if (answer != null) {
125            beginTime(exchange);
126        }
127        return answer;
128    }
129
130    @Override
131    public void after(Exchange exchange, StopWatch watch) throws Exception {
132        // record end time
133        if (watch != null) {
134            recordTime(exchange, watch.taken());
135        }
136    }
137
138    @Override
139    public String toString() {
140        return "InstrumentProcessorAdvice";
141    }
142
143    @Override
144    public int getOrder() {
145        // we want instrumentation before calling the processor (but before tracer/debugger)
146        return Ordered.LOWEST - 2;
147    }
148}