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.mbean;
018
019import java.text.SimpleDateFormat;
020import java.util.Date;
021import java.util.Map;
022
023import org.apache.camel.Exchange;
024import org.apache.camel.api.management.ManagedResource;
025import org.apache.camel.api.management.mbean.ManagedPerformanceCounterMBean;
026import org.apache.camel.management.PerformanceCounter;
027import org.apache.camel.spi.ManagementStrategy;
028import org.apache.camel.support.ExchangeHelper;
029import org.apache.camel.util.json.JsonObject;
030
031@ManagedResource(description = "Managed PerformanceCounter")
032public abstract class ManagedPerformanceCounter extends ManagedCounter
033        implements PerformanceCounter, ManagedPerformanceCounterMBean {
034
035    public static final String TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
036
037    private Statistic exchangesCompleted;
038    private Statistic exchangesFailed;
039    private Statistic exchangesInflight;
040    private Statistic failuresHandled;
041    private Statistic redeliveries;
042    private Statistic externalRedeliveries;
043    private Statistic minProcessingTime;
044    private Statistic maxProcessingTime;
045    private Statistic totalProcessingTime;
046    private Statistic lastProcessingTime;
047    private Statistic deltaProcessingTime;
048    private Statistic meanProcessingTime;
049    private Statistic firstExchangeCompletedTimestamp;
050    private String firstExchangeCompletedExchangeId;
051    private Statistic firstExchangeFailureTimestamp;
052    private String firstExchangeFailureExchangeId;
053    private Statistic lastExchangeCreatedTimestamp;
054    private Statistic lastExchangeCompletedTimestamp;
055    private String lastExchangeCompletedExchangeId;
056    private Statistic lastExchangeFailureTimestamp;
057    private String lastExchangeFailureExchangeId;
058    private boolean statisticsEnabled = true;
059
060    @Override
061    public void init(ManagementStrategy strategy) {
062        super.init(strategy);
063        this.exchangesCompleted = new StatisticCounter();
064        this.exchangesFailed = new StatisticCounter();
065        this.exchangesInflight = new StatisticCounter();
066
067        this.failuresHandled = new StatisticCounter();
068        this.redeliveries = new StatisticCounter();
069        this.externalRedeliveries = new StatisticCounter();
070
071        this.minProcessingTime = new StatisticMinimum();
072        this.maxProcessingTime = new StatisticMaximum();
073        this.totalProcessingTime = new StatisticCounter();
074        this.lastProcessingTime = new StatisticValue();
075        this.deltaProcessingTime = new StatisticDelta();
076        this.meanProcessingTime = new StatisticValue();
077
078        this.firstExchangeCompletedTimestamp = new StatisticValue();
079        this.firstExchangeFailureTimestamp = new StatisticValue();
080        this.lastExchangeCreatedTimestamp = new StatisticValue();
081        this.lastExchangeCompletedTimestamp = new StatisticValue();
082        this.lastExchangeFailureTimestamp = new StatisticValue();
083    }
084
085    @Override
086    public void reset() {
087        super.reset();
088        exchangesCompleted.reset();
089        exchangesFailed.reset();
090        // do not reset exchangesInflight
091        failuresHandled.reset();
092        redeliveries.reset();
093        externalRedeliveries.reset();
094        minProcessingTime.reset();
095        maxProcessingTime.reset();
096        totalProcessingTime.reset();
097        lastProcessingTime.reset();
098        deltaProcessingTime.reset();
099        meanProcessingTime.reset();
100        firstExchangeCompletedTimestamp.reset();
101        firstExchangeCompletedExchangeId = null;
102        firstExchangeFailureTimestamp.reset();
103        firstExchangeFailureExchangeId = null;
104        lastExchangeCreatedTimestamp.reset();
105        lastExchangeCompletedTimestamp.reset();
106        lastExchangeCompletedExchangeId = null;
107        lastExchangeFailureTimestamp.reset();
108        lastExchangeFailureExchangeId = null;
109    }
110
111    @Override
112    public long getExchangesCompleted() {
113        return exchangesCompleted.getValue();
114    }
115
116    @Override
117    public long getExchangesFailed() {
118        return exchangesFailed.getValue();
119    }
120
121    @Override
122    public long getExchangesInflight() {
123        return exchangesInflight.getValue();
124    }
125
126    @Override
127    public long getFailuresHandled() {
128        return failuresHandled.getValue();
129    }
130
131    @Override
132    public long getRedeliveries() {
133        return redeliveries.getValue();
134    }
135
136    @Override
137    public long getExternalRedeliveries() {
138        return externalRedeliveries.getValue();
139    }
140
141    @Override
142    public long getMinProcessingTime() {
143        return minProcessingTime.getValue();
144    }
145
146    @Override
147    public long getMeanProcessingTime() {
148        return meanProcessingTime.getValue();
149    }
150
151    @Override
152    public long getMaxProcessingTime() {
153        return maxProcessingTime.getValue();
154    }
155
156    @Override
157    public long getTotalProcessingTime() {
158        return totalProcessingTime.getValue();
159    }
160
161    @Override
162    public long getLastProcessingTime() {
163        return lastProcessingTime.getValue();
164    }
165
166    @Override
167    public long getDeltaProcessingTime() {
168        return deltaProcessingTime.getValue();
169    }
170
171    @Override
172    public long getIdleSince() {
173        // must not have any inflight
174        if (getExchangesInflight() <= 0) {
175            // what is the last time since completed/failed
176            long max = Math.max(lastExchangeCompletedTimestamp.getValue(), lastExchangeFailureTimestamp.getValue());
177            if (max > 0) {
178                long delta = System.currentTimeMillis() - max;
179                if (delta > 0) {
180                    return delta;
181                }
182            }
183        }
184        return -1;
185    }
186
187    @Override
188    public Date getLastExchangeCreatedTimestamp() {
189        long value = lastExchangeCreatedTimestamp.getValue();
190        return value > 0 ? new Date(value) : null;
191    }
192
193    @Override
194    public Date getLastExchangeCompletedTimestamp() {
195        long value = lastExchangeCompletedTimestamp.getValue();
196        return value > 0 ? new Date(value) : null;
197    }
198
199    @Override
200    public String getLastExchangeCompletedExchangeId() {
201        return lastExchangeCompletedExchangeId;
202    }
203
204    @Override
205    public Date getFirstExchangeCompletedTimestamp() {
206        long value = firstExchangeCompletedTimestamp.getValue();
207        return value > 0 ? new Date(value) : null;
208    }
209
210    @Override
211    public String getFirstExchangeCompletedExchangeId() {
212        return firstExchangeCompletedExchangeId;
213    }
214
215    @Override
216    public Date getLastExchangeFailureTimestamp() {
217        long value = lastExchangeFailureTimestamp.getValue();
218        return value > 0 ? new Date(value) : null;
219    }
220
221    @Override
222    public String getLastExchangeFailureExchangeId() {
223        return lastExchangeFailureExchangeId;
224    }
225
226    @Override
227    public Date getFirstExchangeFailureTimestamp() {
228        long value = firstExchangeFailureTimestamp.getValue();
229        return value > 0 ? new Date(value) : null;
230    }
231
232    @Override
233    public String getFirstExchangeFailureExchangeId() {
234        return firstExchangeFailureExchangeId;
235    }
236
237    @Override
238    public boolean isStatisticsEnabled() {
239        return statisticsEnabled;
240    }
241
242    @Override
243    public void setStatisticsEnabled(boolean statisticsEnabled) {
244        this.statisticsEnabled = statisticsEnabled;
245    }
246
247    @Override
248    public void processExchange(Exchange exchange, String type) {
249        exchangesInflight.increment();
250        if ("route".equals(type)) {
251            long now = System.currentTimeMillis();
252            lastExchangeCreatedTimestamp.updateValue(now);
253        }
254    }
255
256    @Override
257    public void completedExchange(Exchange exchange, long time) {
258        increment();
259        exchangesCompleted.increment();
260        exchangesInflight.decrement();
261
262        if (ExchangeHelper.isFailureHandled(exchange)) {
263            failuresHandled.increment();
264        }
265        if (exchange.isExternalRedelivered()) {
266            externalRedeliveries.increment();
267        }
268
269        minProcessingTime.updateValue(time);
270        maxProcessingTime.updateValue(time);
271        totalProcessingTime.updateValue(time);
272        lastProcessingTime.updateValue(time);
273        deltaProcessingTime.updateValue(time);
274
275        long now = System.currentTimeMillis();
276        if (!firstExchangeCompletedTimestamp.isUpdated()) {
277            firstExchangeCompletedTimestamp.updateValue(now);
278        }
279
280        lastExchangeCompletedTimestamp.updateValue(now);
281        if (firstExchangeCompletedExchangeId == null) {
282            firstExchangeCompletedExchangeId = exchange.getExchangeId();
283        }
284        lastExchangeCompletedExchangeId = exchange.getExchangeId();
285
286        // update mean
287        long mean = 0;
288        long completed = exchangesCompleted.getValue();
289        if (completed > 0) {
290            mean = totalProcessingTime.getValue() / completed;
291        }
292        meanProcessingTime.updateValue(mean);
293    }
294
295    @Override
296    public void failedExchange(Exchange exchange) {
297        increment();
298        exchangesFailed.increment();
299        exchangesInflight.decrement();
300
301        if (ExchangeHelper.isRedelivered(exchange)) {
302            redeliveries.increment();
303        }
304        if (exchange.isExternalRedelivered()) {
305            externalRedeliveries.increment();
306        }
307
308        long now = System.currentTimeMillis();
309        if (!firstExchangeFailureTimestamp.isUpdated()) {
310            firstExchangeFailureTimestamp.updateValue(now);
311        }
312
313        lastExchangeFailureTimestamp.updateValue(now);
314        if (firstExchangeFailureExchangeId == null) {
315            firstExchangeFailureExchangeId = exchange.getExchangeId();
316        }
317        lastExchangeFailureExchangeId = exchange.getExchangeId();
318    }
319
320    @Override
321    public String dumpStatsAsXml(boolean fullStats) {
322        StringBuilder sb = new StringBuilder();
323        sb.append("<stats ");
324        sb.append(String.format("exchangesTotal=\"%s\"", exchangesTotal.getValue()));
325        sb.append(String.format(" exchangesCompleted=\"%s\"", exchangesCompleted.getValue()));
326        sb.append(String.format(" exchangesFailed=\"%s\"", exchangesFailed.getValue()));
327        sb.append(String.format(" failuresHandled=\"%s\"", failuresHandled.getValue()));
328        sb.append(String.format(" redeliveries=\"%s\"", redeliveries.getValue()));
329        sb.append(String.format(" externalRedeliveries=\"%s\"", externalRedeliveries.getValue()));
330        sb.append(String.format(" minProcessingTime=\"%s\"", minProcessingTime.getValue()));
331        sb.append(String.format(" maxProcessingTime=\"%s\"", maxProcessingTime.getValue()));
332        sb.append(String.format(" totalProcessingTime=\"%s\"", totalProcessingTime.getValue()));
333        sb.append(String.format(" lastProcessingTime=\"%s\"", lastProcessingTime.getValue()));
334        sb.append(String.format(" deltaProcessingTime=\"%s\"", deltaProcessingTime.getValue()));
335        sb.append(String.format(" meanProcessingTime=\"%s\"", meanProcessingTime.getValue()));
336        sb.append(String.format(" idleSince=\"%s\"", getIdleSince()));
337
338        if (fullStats) {
339            sb.append(String.format(" startTimestamp=\"%s\"", dateAsString(startTimestamp.getTime())));
340            sb.append(String.format(" resetTimestamp=\"%s\"", dateAsString(resetTimestamp.getTime())));
341            sb.append(String.format(" firstExchangeCompletedTimestamp=\"%s\"",
342                    dateAsString(firstExchangeCompletedTimestamp.getValue())));
343            sb.append(String.format(" firstExchangeCompletedExchangeId=\"%s\"", nullSafe(firstExchangeCompletedExchangeId)));
344            sb.append(String.format(" firstExchangeFailureTimestamp=\"%s\"",
345                    dateAsString(firstExchangeFailureTimestamp.getValue())));
346            sb.append(String.format(" firstExchangeFailureExchangeId=\"%s\"", nullSafe(firstExchangeFailureExchangeId)));
347            sb.append(String.format(" lastExchangeCreatedTimestamp=\"%s\"",
348                    dateAsString(lastExchangeCreatedTimestamp.getValue())));
349            sb.append(String.format(" lastExchangeCompletedTimestamp=\"%s\"",
350                    dateAsString(lastExchangeCompletedTimestamp.getValue())));
351            sb.append(String.format(" lastExchangeCompletedExchangeId=\"%s\"", nullSafe(lastExchangeCompletedExchangeId)));
352            sb.append(String.format(" lastExchangeFailureTimestamp=\"%s\"",
353                    dateAsString(lastExchangeFailureTimestamp.getValue())));
354            sb.append(String.format(" lastExchangeFailureExchangeId=\"%s\"", nullSafe(lastExchangeFailureExchangeId)));
355        }
356        sb.append("/>");
357        return sb.toString();
358    }
359
360    @Override
361    public String dumpStatsAsJSon(boolean fullStats) {
362        JsonObject jo = new JsonObject();
363        statsAsJSon(jo, fullStats);
364        return jo.toJson();
365    }
366
367    @Override
368    public void statsAsJSon(Map<String, Object> jo, boolean fullStats) {
369        jo.put("exchangesTotal", exchangesTotal.getValue());
370        jo.put("exchangesCompleted", exchangesCompleted.getValue());
371        jo.put("exchangesFailed", exchangesFailed.getValue());
372        jo.put("failuresHandled", failuresHandled.getValue());
373        jo.put("redeliveries", redeliveries.getValue());
374        jo.put("externalRedeliveries", externalRedeliveries.getValue());
375        jo.put("minProcessingTime", minProcessingTime.getValue());
376        jo.put("maxProcessingTime", maxProcessingTime.getValue());
377        jo.put("totalProcessingTime", totalProcessingTime.getValue());
378        jo.put("lastProcessingTime", lastProcessingTime.getValue());
379        jo.put("deltaProcessingTime", deltaProcessingTime.getValue());
380        jo.put("meanProcessingTime", meanProcessingTime.getValue());
381        jo.put("idleSince", getIdleSince());
382        if (fullStats) {
383            jo.put("startTimestamp", startTimestamp.getTime());
384            jo.put("resetTimestamp", resetTimestamp.getTime());
385            jo.put("firstExchangeCompletedTimestamp", firstExchangeCompletedTimestamp.getValue());
386            jo.put("firstExchangeCompletedExchangeId", firstExchangeCompletedExchangeId);
387            jo.put("firstExchangeFailureTimestamp", firstExchangeFailureTimestamp.getValue());
388            jo.put("firstExchangeFailureExchangeId", firstExchangeFailureExchangeId);
389            jo.put("lastExchangeCreatedTimestamp", lastExchangeCreatedTimestamp.getValue());
390            jo.put("lastExchangeCompletedTimestamp", lastExchangeCompletedTimestamp.getValue());
391            jo.put("lastExchangeCompletedExchangeId", lastExchangeCompletedExchangeId);
392            jo.put("lastExchangeFailureTimestamp", lastExchangeFailureTimestamp.getValue());
393            jo.put("lastExchangeFailureExchangeId", lastExchangeFailureExchangeId);
394        }
395    }
396
397    private static String dateAsString(long value) {
398        if (value <= 0) {
399            return "";
400        }
401        return new SimpleDateFormat(TIMESTAMP_FORMAT).format(value);
402    }
403
404    private static String nullSafe(String s) {
405        return s != null ? s : "";
406    }
407
408}