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    @Override
053    public void setCounter(Object counter) {
054        ManagedPerformanceCounter mpc = null;
055        if (counter instanceof ManagedPerformanceCounter) {
056            mpc = (ManagedPerformanceCounter) counter;
057        }
058
059        if (this.counter instanceof DelegatePerformanceCounter) {
060            ((DelegatePerformanceCounter) this.counter).setCounter(mpc);
061        } else if (mpc != null) {
062            this.counter = mpc;
063        } else if (counter instanceof PerformanceCounter) {
064            this.counter = (PerformanceCounter) counter;
065        }
066    }
067
068    @Override
069    public boolean process(final Exchange exchange, final AsyncCallback callback) {
070        // only record time if stats is enabled
071        final StopWatch watch = (counter != null && counter.isStatisticsEnabled()) ? new StopWatch() : null;
072
073        // mark beginning to process the exchange
074        if (watch != null) {
075            beginTime(exchange);
076        }
077
078        return processor.process(exchange, new AsyncCallback() {
079            public void done(boolean doneSync) {
080                try {
081                    // record end time
082                    if (watch != null) {
083                        recordTime(exchange, watch.taken());
084                    }
085                } finally {
086                    // and let the original callback know we are done as well
087                    callback.done(doneSync);
088                }
089            }
090
091            @Override
092            public String toString() {
093                return DefaultInstrumentationProcessor.this.toString();
094            }
095        });
096    }
097
098    protected void beginTime(Exchange exchange) {
099        counter.processExchange(exchange);
100    }
101
102    protected void recordTime(Exchange exchange, long duration) {
103        if (log.isTraceEnabled()) {
104            log.trace("{}Recording duration: {} millis for exchange: {}", type != null ? type + ": " : "", duration, exchange);
105        }
106
107        if (!exchange.isFailed() && exchange.getException() == null) {
108            counter.completedExchange(exchange, duration);
109        } else {
110            counter.failedExchange(exchange);
111        }
112    }
113
114    public String getType() {
115        return type;
116    }
117
118    public void setType(String type) {
119        this.type = type;
120    }
121
122    @Override
123    public StopWatch before(Exchange exchange) throws Exception {
124        // only record time if stats is enabled
125        StopWatch answer = counter != null && counter.isStatisticsEnabled() ? new StopWatch() : null;
126        if (answer != null) {
127            beginTime(exchange);
128        }
129        return answer;
130    }
131
132    @Override
133    public void after(Exchange exchange, StopWatch watch) throws Exception {
134        // record end time
135        if (watch != null) {
136            recordTime(exchange, watch.taken());
137        }
138    }
139
140    @Override
141    public String toString() {
142        return "InstrumentProcessorAdvice";
143    }
144
145    @Override
146    public int getOrder() {
147        // we want instrumentation before calling the processor (but before tracer/debugger)
148        return Ordered.LOWEST - 2;
149    }
150}