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