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