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.io.Serializable;
020import java.util.ArrayList;
021import java.util.Collections;
022import java.util.Comparator;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026import java.util.Set;
027import java.util.concurrent.RejectedExecutionException;
028import java.util.concurrent.TimeUnit;
029
030import javax.management.AttributeValueExp;
031import javax.management.MBeanServer;
032import javax.management.ObjectName;
033import javax.management.Query;
034import javax.management.QueryExp;
035import javax.management.StringValueExp;
036import javax.management.openmbean.CompositeData;
037import javax.management.openmbean.CompositeDataSupport;
038import javax.management.openmbean.CompositeType;
039import javax.management.openmbean.TabularData;
040import javax.management.openmbean.TabularDataSupport;
041
042import org.apache.camel.CamelContext;
043import org.apache.camel.ExtendedCamelContext;
044import org.apache.camel.ManagementStatisticsLevel;
045import org.apache.camel.Route;
046import org.apache.camel.RuntimeCamelException;
047import org.apache.camel.ServiceStatus;
048import org.apache.camel.TimerListener;
049import org.apache.camel.api.management.ManagedResource;
050import org.apache.camel.api.management.mbean.CamelOpenMBeanTypes;
051import org.apache.camel.api.management.mbean.ManagedProcessorMBean;
052import org.apache.camel.api.management.mbean.ManagedRouteMBean;
053import org.apache.camel.api.management.mbean.ManagedStepMBean;
054import org.apache.camel.api.management.mbean.RouteError;
055import org.apache.camel.model.Model;
056import org.apache.camel.model.ModelCamelContext;
057import org.apache.camel.model.RouteDefinition;
058import org.apache.camel.spi.InflightRepository;
059import org.apache.camel.spi.ManagementStrategy;
060import org.apache.camel.spi.RoutePolicy;
061import org.apache.camel.util.ObjectHelper;
062import org.slf4j.Logger;
063import org.slf4j.LoggerFactory;
064
065@ManagedResource(description = "Managed Route")
066public class ManagedRoute extends ManagedPerformanceCounter implements TimerListener, ManagedRouteMBean {
067
068    public static final String VALUE_UNKNOWN = "Unknown";
069
070    private static final Logger LOG = LoggerFactory.getLogger(ManagedRoute.class);
071
072    protected final Route route;
073    protected final String description;
074    protected final String configurationId;
075    protected final String sourceLocation;
076    protected final CamelContext context;
077    private final LoadTriplet load = new LoadTriplet();
078    private final String jmxDomain;
079
080    public ManagedRoute(CamelContext context, Route route) {
081        this.route = route;
082        this.context = context;
083        this.description = route.getDescription();
084        this.configurationId = route.getConfigurationId();
085        this.sourceLocation = route.getSourceResource() != null ? route.getSourceResource().getLocation() : null;
086        this.jmxDomain = context.getManagementStrategy().getManagementAgent().getMBeanObjectDomainName();
087    }
088
089    @Override
090    public void init(ManagementStrategy strategy) {
091        super.init(strategy);
092        boolean enabled
093                = context.getManagementStrategy().getManagementAgent().getStatisticsLevel() != ManagementStatisticsLevel.Off;
094        setStatisticsEnabled(enabled);
095    }
096
097    public Route getRoute() {
098        return route;
099    }
100
101    public CamelContext getContext() {
102        return context;
103    }
104
105    @Override
106    public String getRouteId() {
107        String id = route.getId();
108        if (id == null) {
109            id = VALUE_UNKNOWN;
110        }
111        return id;
112    }
113
114    @Override
115    public String getRouteGroup() {
116        return route.getGroup();
117    }
118
119    @Override
120    public TabularData getRouteProperties() {
121        try {
122            final Map<String, Object> properties = route.getProperties();
123            final TabularData answer = new TabularDataSupport(CamelOpenMBeanTypes.camelRoutePropertiesTabularType());
124            final CompositeType ct = CamelOpenMBeanTypes.camelRoutePropertiesCompositeType();
125
126            // gather route properties
127            for (Map.Entry<String, Object> entry : properties.entrySet()) {
128                final String key = entry.getKey();
129                final String val = context.getTypeConverter().convertTo(String.class, entry.getValue());
130
131                CompositeData data = new CompositeDataSupport(
132                        ct,
133                        new String[] { "key", "value" },
134                        new Object[] { key, val });
135
136                answer.put(data);
137            }
138            return answer;
139        } catch (Exception e) {
140            throw RuntimeCamelException.wrapRuntimeCamelException(e);
141        }
142    }
143
144    @Override
145    public String getDescription() {
146        return description;
147    }
148
149    @Override
150    public String getSourceLocation() {
151        return sourceLocation;
152    }
153
154    @Override
155    public String getRouteConfigurationId() {
156        return configurationId;
157    }
158
159    @Override
160    public String getEndpointUri() {
161        if (route.getEndpoint() != null) {
162            return route.getEndpoint().getEndpointUri();
163        }
164        return VALUE_UNKNOWN;
165    }
166
167    @Override
168    public String getState() {
169        // must use String type to be sure remote JMX can read the attribute without requiring Camel classes.
170        ServiceStatus status = context.getRouteController().getRouteStatus(route.getId());
171        // if no status exists then its stopped
172        if (status == null) {
173            status = ServiceStatus.Stopped;
174        }
175        return status.name();
176    }
177
178    @Override
179    public String getUptime() {
180        return route.getUptime();
181    }
182
183    @Override
184    public long getUptimeMillis() {
185        return route.getUptimeMillis();
186    }
187
188    @Override
189    public String getCamelId() {
190        return context.getName();
191    }
192
193    @Override
194    public String getCamelManagementName() {
195        return context.getManagementName();
196    }
197
198    @Override
199    public Boolean getTracing() {
200        return route.isTracing();
201    }
202
203    @Override
204    public void setTracing(Boolean tracing) {
205        route.setTracing(tracing);
206    }
207
208    @Override
209    public Boolean getMessageHistory() {
210        return route.isMessageHistory();
211    }
212
213    @Override
214    public Boolean getLogMask() {
215        return route.isLogMask();
216    }
217
218    @Override
219    public String getRoutePolicyList() {
220        List<RoutePolicy> policyList = route.getRoutePolicyList();
221
222        if (policyList == null || policyList.isEmpty()) {
223            // return an empty string to have it displayed nicely in JMX consoles
224            return "";
225        }
226
227        StringBuilder sb = new StringBuilder();
228        for (int i = 0; i < policyList.size(); i++) {
229            RoutePolicy policy = policyList.get(i);
230            sb.append(policy.getClass().getSimpleName());
231            sb.append("(").append(ObjectHelper.getIdentityHashCode(policy)).append(")");
232            if (i < policyList.size() - 1) {
233                sb.append(", ");
234            }
235        }
236        return sb.toString();
237    }
238
239    @Override
240    public String getLoad01() {
241        double load1 = load.getLoad1();
242        if (Double.isNaN(load1)) {
243            // empty string if load statistics is disabled
244            return "";
245        } else {
246            return String.format("%.2f", load1);
247        }
248    }
249
250    @Override
251    public String getLoad05() {
252        double load5 = load.getLoad5();
253        if (Double.isNaN(load5)) {
254            // empty string if load statistics is disabled
255            return "";
256        } else {
257            return String.format("%.2f", load5);
258        }
259    }
260
261    @Override
262    public String getLoad15() {
263        double load15 = load.getLoad15();
264        if (Double.isNaN(load15)) {
265            // empty string if load statistics is disabled
266            return "";
267        } else {
268            return String.format("%.2f", load15);
269        }
270    }
271
272    @Override
273    public void onTimer() {
274        load.update(getInflightExchanges());
275    }
276
277    @Override
278    public void start() throws Exception {
279        if (!context.getStatus().isStarted()) {
280            throw new IllegalArgumentException("CamelContext is not started");
281        }
282        context.getRouteController().startRoute(getRouteId());
283    }
284
285    @Override
286    public void stop() throws Exception {
287        if (!context.getStatus().isStarted()) {
288            throw new IllegalArgumentException("CamelContext is not started");
289        }
290        context.getRouteController().stopRoute(getRouteId());
291    }
292
293    @Override
294    public void stopAndFail() throws Exception {
295        if (!context.getStatus().isStarted()) {
296            throw new IllegalArgumentException("CamelContext is not started");
297        }
298        Throwable cause = new RejectedExecutionException("Route " + getRouteId() + " is forced stopped and marked as failed");
299        context.getRouteController().stopRoute(getRouteId(), cause);
300    }
301
302    @Override
303    public void stop(long timeout) throws Exception {
304        if (!context.getStatus().isStarted()) {
305            throw new IllegalArgumentException("CamelContext is not started");
306        }
307        context.getRouteController().stopRoute(getRouteId(), timeout, TimeUnit.SECONDS);
308    }
309
310    @Override
311    public boolean stop(Long timeout, Boolean abortAfterTimeout) throws Exception {
312        if (!context.getStatus().isStarted()) {
313            throw new IllegalArgumentException("CamelContext is not started");
314        }
315        return context.getRouteController().stopRoute(getRouteId(), timeout, TimeUnit.SECONDS, abortAfterTimeout);
316    }
317
318    public void shutdown() throws Exception {
319        if (!context.getStatus().isStarted()) {
320            throw new IllegalArgumentException("CamelContext is not started");
321        }
322        String routeId = getRouteId();
323        context.getRouteController().stopRoute(routeId);
324        context.removeRoute(routeId);
325    }
326
327    public void shutdown(long timeout) throws Exception {
328        if (!context.getStatus().isStarted()) {
329            throw new IllegalArgumentException("CamelContext is not started");
330        }
331        String routeId = getRouteId();
332        context.getRouteController().stopRoute(routeId, timeout, TimeUnit.SECONDS);
333        context.removeRoute(routeId);
334    }
335
336    @Override
337    public boolean remove() throws Exception {
338        if (!context.getStatus().isStarted()) {
339            throw new IllegalArgumentException("CamelContext is not started");
340        }
341        return context.removeRoute(getRouteId());
342    }
343
344    @Override
345    public void restart() throws Exception {
346        restart(1);
347    }
348
349    @Override
350    public void restart(long delay) throws Exception {
351        stop();
352        if (delay > 0) {
353            try {
354                LOG.debug("Sleeping {} seconds before starting route: {}", delay, getRouteId());
355                Thread.sleep(delay * 1000);
356            } catch (InterruptedException e) {
357                // ignore
358            }
359        }
360        start();
361    }
362
363    @Override
364    public String dumpRouteAsXml() throws Exception {
365        return dumpRouteAsXml(false, false);
366    }
367
368    @Override
369    public String dumpRouteAsXml(boolean resolvePlaceholders) throws Exception {
370        return dumpRouteAsXml(resolvePlaceholders, false);
371    }
372
373    @Override
374    public String dumpRouteAsXml(boolean resolvePlaceholders, boolean resolveDelegateEndpoints) throws Exception {
375        String id = route.getId();
376        RouteDefinition def = context.getExtension(Model.class).getRouteDefinition(id);
377        if (def != null) {
378            ExtendedCamelContext ecc = context.adapt(ExtendedCamelContext.class);
379            return ecc.getModelToXMLDumper().dumpModelAsXml(context, def, resolvePlaceholders, resolveDelegateEndpoints);
380        }
381
382        return null;
383    }
384
385    @Override
386    public String dumpRouteStatsAsXml(boolean fullStats, boolean includeProcessors) throws Exception {
387        // in this logic we need to calculate the accumulated processing time for the processor in the route
388        // and hence why the logic is a bit more complicated to do this, as we need to calculate that from
389        // the bottom -> top of the route but this information is valuable for profiling routes
390        StringBuilder sb = new StringBuilder();
391
392        // need to calculate this value first, as we need that value for the route stat
393        long processorAccumulatedTime = 0L;
394
395        // gather all the processors for this route, which requires JMX
396        if (includeProcessors) {
397            sb.append("  <processorStats>\n");
398            MBeanServer server = getContext().getManagementStrategy().getManagementAgent().getMBeanServer();
399            if (server != null) {
400                // get all the processor mbeans and sort them accordingly to their index
401                String prefix = getContext().getManagementStrategy().getManagementAgent().getIncludeHostName() ? "*/" : "";
402                ObjectName query = ObjectName.getInstance(
403                        jmxDomain + ":context=" + prefix + getContext().getManagementName() + ",type=processors,*");
404                Set<ObjectName> names = server.queryNames(query, null);
405                List<ManagedProcessorMBean> mps = new ArrayList<>();
406                for (ObjectName on : names) {
407                    ManagedProcessorMBean processor = context.getManagementStrategy().getManagementAgent().newProxyClient(on,
408                            ManagedProcessorMBean.class);
409
410                    // the processor must belong to this route
411                    if (getRouteId().equals(processor.getRouteId())) {
412                        mps.add(processor);
413                    }
414                }
415                mps.sort(new OrderProcessorMBeans());
416
417                // walk the processors in reverse order, and calculate the accumulated total time
418                Map<String, Long> accumulatedTimes = new HashMap<>();
419                Collections.reverse(mps);
420                for (ManagedProcessorMBean processor : mps) {
421                    processorAccumulatedTime += processor.getTotalProcessingTime();
422                    accumulatedTimes.put(processor.getProcessorId(), processorAccumulatedTime);
423                }
424                // and reverse back again
425                Collections.reverse(mps);
426
427                // and now add the sorted list of processors to the xml output
428                for (ManagedProcessorMBean processor : mps) {
429                    int line = processor.getSourceLineNumber() != null ? processor.getSourceLineNumber() : -1;
430                    sb.append("    <processorStat")
431                            .append(String.format(" id=\"%s\" index=\"%s\" state=\"%s\" sourceLineNumber=\"%s\"",
432                                    processor.getProcessorId(), processor.getIndex(), processor.getState(), line));
433                    // do we have an accumulated time then append that
434                    Long accTime = accumulatedTimes.get(processor.getProcessorId());
435                    if (accTime != null) {
436                        sb.append(" accumulatedProcessingTime=\"").append(accTime).append("\"");
437                    }
438                    // use substring as we only want the attributes
439                    sb.append(" ").append(processor.dumpStatsAsXml(fullStats).substring(7)).append("\n");
440                }
441            }
442            sb.append("  </processorStats>\n");
443        }
444
445        // route self time is route total - processor accumulated total)
446        long routeSelfTime = getTotalProcessingTime() - processorAccumulatedTime;
447        if (routeSelfTime < 0) {
448            // ensure we don't calculate that as negative
449            routeSelfTime = 0;
450        }
451
452        StringBuilder answer = new StringBuilder();
453        answer.append("<routeStat").append(String.format(" id=\"%s\"", route.getId()))
454                .append(String.format(" state=\"%s\"", getState()));
455        if (sourceLocation != null) {
456            answer.append(String.format(" sourceLocation=\"%s\"", getSourceLocation()));
457        }
458        // use substring as we only want the attributes
459        String stat = dumpStatsAsXml(fullStats);
460        answer.append(" exchangesInflight=\"").append(getInflightExchanges()).append("\"");
461        answer.append(" selfProcessingTime=\"").append(routeSelfTime).append("\"");
462        InflightRepository.InflightExchange oldest = getOldestInflightEntry();
463        if (oldest == null) {
464            answer.append(" oldestInflightExchangeId=\"\"");
465            answer.append(" oldestInflightDuration=\"\"");
466        } else {
467            answer.append(" oldestInflightExchangeId=\"").append(oldest.getExchange().getExchangeId()).append("\"");
468            answer.append(" oldestInflightDuration=\"").append(oldest.getDuration()).append("\"");
469        }
470        answer.append(" ").append(stat, 7, stat.length() - 2).append(">\n");
471
472        if (includeProcessors) {
473            answer.append(sb);
474        }
475
476        answer.append("</routeStat>");
477        return answer.toString();
478    }
479
480    @Override
481    public String dumpStepStatsAsXml(boolean fullStats) throws Exception {
482        // in this logic we need to calculate the accumulated processing time for the processor in the route
483        // and hence why the logic is a bit more complicated to do this, as we need to calculate that from
484        // the bottom -> top of the route but this information is valuable for profiling routes
485        StringBuilder sb = new StringBuilder();
486
487        // gather all the steps for this route, which requires JMX
488        sb.append("  <stepStats>\n");
489        MBeanServer server = getContext().getManagementStrategy().getManagementAgent().getMBeanServer();
490        if (server != null) {
491            // get all the processor mbeans and sort them accordingly to their index
492            String prefix = getContext().getManagementStrategy().getManagementAgent().getIncludeHostName() ? "*/" : "";
493            ObjectName query = ObjectName
494                    .getInstance(jmxDomain + ":context=" + prefix + getContext().getManagementName() + ",type=steps,*");
495            Set<ObjectName> names = server.queryNames(query, null);
496            List<ManagedStepMBean> mps = new ArrayList<>();
497            for (ObjectName on : names) {
498                ManagedStepMBean step
499                        = context.getManagementStrategy().getManagementAgent().newProxyClient(on, ManagedStepMBean.class);
500
501                // the step must belong to this route
502                if (getRouteId().equals(step.getRouteId())) {
503                    mps.add(step);
504                }
505            }
506            mps.sort(new OrderProcessorMBeans());
507
508            // and now add the sorted list of steps to the xml output
509            for (ManagedStepMBean step : mps) {
510                int line = step.getSourceLineNumber() != null ? step.getSourceLineNumber() : -1;
511                sb.append("    <stepStat")
512                        .append(String.format(" id=\"%s\" index=\"%s\" state=\"%s\" sourceLineNumber=\"%s\"",
513                                step.getProcessorId(),
514                                step.getIndex(), step.getState(), line));
515                // use substring as we only want the attributes
516                sb.append(" ").append(step.dumpStatsAsXml(fullStats).substring(7)).append("\n");
517            }
518        }
519        sb.append("  </stepStats>\n");
520
521        StringBuilder answer = new StringBuilder();
522        answer.append("<routeStat").append(String.format(" id=\"%s\"", route.getId()))
523                .append(String.format(" state=\"%s\"", getState()));
524        if (sourceLocation != null) {
525            answer.append(String.format(" sourceLocation=\"%s\"", getSourceLocation()));
526        }
527        // use substring as we only want the attributes
528        String stat = dumpStatsAsXml(fullStats);
529        answer.append(" exchangesInflight=\"").append(getInflightExchanges()).append("\"");
530        InflightRepository.InflightExchange oldest = getOldestInflightEntry();
531        if (oldest == null) {
532            answer.append(" oldestInflightExchangeId=\"\"");
533            answer.append(" oldestInflightDuration=\"\"");
534        } else {
535            answer.append(" oldestInflightExchangeId=\"").append(oldest.getExchange().getExchangeId()).append("\"");
536            answer.append(" oldestInflightDuration=\"").append(oldest.getDuration()).append("\"");
537        }
538        answer.append(" ").append(stat, 7, stat.length() - 2).append(">\n");
539
540        answer.append(sb);
541
542        answer.append("</routeStat>");
543        return answer.toString();
544    }
545
546    @Override
547    public String dumpRouteSourceLocationsAsXml() throws Exception {
548        StringBuilder sb = new StringBuilder();
549        sb.append("<routeLocations>");
550
551        MBeanServer server = getContext().getManagementStrategy().getManagementAgent().getMBeanServer();
552        if (server != null) {
553            String prefix = getContext().getManagementStrategy().getManagementAgent().getIncludeHostName() ? "*/" : "";
554            List<ManagedProcessorMBean> processors = new ArrayList<>();
555            // gather all the processors for this CamelContext, which requires JMX
556            ObjectName query = ObjectName
557                    .getInstance(jmxDomain + ":context=" + prefix + getContext().getManagementName() + ",type=processors,*");
558            Set<ObjectName> names = server.queryNames(query, null);
559            for (ObjectName on : names) {
560                ManagedProcessorMBean processor
561                        = context.getManagementStrategy().getManagementAgent().newProxyClient(on, ManagedProcessorMBean.class);
562                // the processor must belong to this route
563                if (getRouteId().equals(processor.getRouteId())) {
564                    processors.add(processor);
565                }
566            }
567            processors.sort(new OrderProcessorMBeans());
568
569            // grab route consumer
570            RouteDefinition rd = context.adapt(ModelCamelContext.class).getRouteDefinition(route.getRouteId());
571            if (rd != null) {
572                String id = rd.getRouteId();
573                int line = rd.getInput().getLineNumber();
574                String location = getSourceLocation() != null ? getSourceLocation() : "";
575                sb.append("\n    <routeLocation")
576                        .append(String.format(
577                                " routeId=\"%s\" id=\"%s\" index=\"%s\" sourceLocation=\"%s\" sourceLineNumber=\"%s\"/>",
578                                route.getRouteId(), id, 0, location, line));
579            }
580            for (ManagedProcessorMBean processor : processors) {
581                // the step must belong to this route
582                if (route.getRouteId().equals(processor.getRouteId())) {
583                    int line = processor.getSourceLineNumber() != null ? processor.getSourceLineNumber() : -1;
584                    String location = processor.getSourceLocation() != null ? processor.getSourceLocation() : "";
585                    sb.append("\n    <routeLocation")
586                            .append(String.format(
587                                    " routeId=\"%s\" id=\"%s\" index=\"%s\" sourceLocation=\"%s\" sourceLineNumber=\"%s\"/>",
588                                    route.getRouteId(), processor.getProcessorId(), processor.getIndex(), location, line));
589                }
590            }
591        }
592        sb.append("\n</routeLocations>");
593        return sb.toString();
594    }
595
596    @Override
597    public void reset(boolean includeProcessors) throws Exception {
598        reset();
599
600        // and now reset all processors for this route
601        if (includeProcessors) {
602            MBeanServer server = getContext().getManagementStrategy().getManagementAgent().getMBeanServer();
603            if (server != null) {
604                // get all the processor mbeans and sort them accordingly to their index
605                String prefix = getContext().getManagementStrategy().getManagementAgent().getIncludeHostName() ? "*/" : "";
606                ObjectName query = ObjectName.getInstance(
607                        jmxDomain + ":context=" + prefix + getContext().getManagementName() + ",type=processors,*");
608                QueryExp queryExp = Query.match(new AttributeValueExp("RouteId"), new StringValueExp(getRouteId()));
609                Set<ObjectName> names = server.queryNames(query, queryExp);
610                for (ObjectName name : names) {
611                    server.invoke(name, "reset", null, null);
612                }
613            }
614        }
615    }
616
617    @Override
618    public boolean equals(Object o) {
619        return this == o || o != null && getClass() == o.getClass() && route.equals(((ManagedRoute) o).route);
620    }
621
622    @Override
623    public int hashCode() {
624        return route.hashCode();
625    }
626
627    private InflightRepository.InflightExchange getOldestInflightEntry() {
628        return getContext().getInflightRepository().oldest(getRouteId());
629    }
630
631    @Override
632    public Long getOldestInflightDuration() {
633        InflightRepository.InflightExchange oldest = getOldestInflightEntry();
634        if (oldest == null) {
635            return null;
636        } else {
637            return oldest.getDuration();
638        }
639    }
640
641    @Override
642    public String getOldestInflightExchangeId() {
643        InflightRepository.InflightExchange oldest = getOldestInflightEntry();
644        if (oldest == null) {
645            return null;
646        } else {
647            return oldest.getExchange().getExchangeId();
648        }
649    }
650
651    @Override
652    public Boolean getHasRouteController() {
653        return route.getRouteController() != null;
654    }
655
656    @Override
657    public RouteError getLastError() {
658        org.apache.camel.spi.RouteError error = route.getLastError();
659        if (error == null) {
660            return null;
661        } else {
662            return new RouteError() {
663                @Override
664                public Phase getPhase() {
665                    if (error.getPhase() != null) {
666                        switch (error.getPhase()) {
667                            case START:
668                                return Phase.START;
669                            case STOP:
670                                return Phase.STOP;
671                            case SUSPEND:
672                                return Phase.SUSPEND;
673                            case RESUME:
674                                return Phase.RESUME;
675                            case SHUTDOWN:
676                                return Phase.SHUTDOWN;
677                            case REMOVE:
678                                return Phase.REMOVE;
679                            default:
680                                throw new IllegalStateException();
681                        }
682                    }
683                    return null;
684                }
685
686                @Override
687                public Throwable getException() {
688                    return error.getException();
689                }
690            };
691        }
692    }
693
694    private Integer getInflightExchanges() {
695        return (int) super.getExchangesInflight();
696    }
697
698    /**
699     * Used for sorting the processor mbeans accordingly to their index.
700     */
701    private static final class OrderProcessorMBeans implements Comparator<ManagedProcessorMBean>, Serializable {
702
703        @Override
704        public int compare(ManagedProcessorMBean o1, ManagedProcessorMBean o2) {
705            return o1.getIndex().compareTo(o2.getIndex());
706        }
707    }
708}