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