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