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