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