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.IOException; 020import java.io.InputStream; 021import java.net.URLDecoder; 022import java.util.ArrayList; 023import java.util.Collection; 024import java.util.Collections; 025import java.util.Comparator; 026import java.util.List; 027import java.util.Map; 028import java.util.Properties; 029import java.util.Set; 030import java.util.concurrent.TimeUnit; 031import javax.management.MBeanServer; 032import javax.management.MBeanServerInvocationHandler; 033import javax.management.ObjectName; 034import javax.management.openmbean.CompositeData; 035import javax.management.openmbean.CompositeDataSupport; 036import javax.management.openmbean.CompositeType; 037import javax.management.openmbean.TabularData; 038import javax.management.openmbean.TabularDataSupport; 039 040import org.apache.camel.CamelContext; 041import org.apache.camel.Component; 042import org.apache.camel.ComponentConfiguration; 043import org.apache.camel.Endpoint; 044import org.apache.camel.ManagementStatisticsLevel; 045import org.apache.camel.Producer; 046import org.apache.camel.ProducerTemplate; 047import org.apache.camel.Route; 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.ManagedCamelContextMBean; 052import org.apache.camel.api.management.mbean.ManagedProcessorMBean; 053import org.apache.camel.api.management.mbean.ManagedRouteMBean; 054import org.apache.camel.model.ModelCamelContext; 055import org.apache.camel.model.ModelHelper; 056import org.apache.camel.model.RouteDefinition; 057import org.apache.camel.model.RoutesDefinition; 058import org.apache.camel.model.rest.RestDefinition; 059import org.apache.camel.model.rest.RestsDefinition; 060import org.apache.camel.util.CamelContextHelper; 061import org.apache.camel.util.JsonSchemaHelper; 062import org.apache.camel.util.ObjectHelper; 063 064/** 065 * @version 066 */ 067@ManagedResource(description = "Managed CamelContext") 068public class ManagedCamelContext extends ManagedPerformanceCounter implements TimerListener, ManagedCamelContextMBean { 069 private final ModelCamelContext context; 070 private final LoadTriplet load = new LoadTriplet(); 071 072 public ManagedCamelContext(ModelCamelContext context) { 073 this.context = context; 074 boolean enabled = context.getManagementStrategy().getStatisticsLevel() != ManagementStatisticsLevel.Off; 075 setStatisticsEnabled(enabled); 076 } 077 078 public CamelContext getContext() { 079 return context; 080 } 081 082 public String getCamelId() { 083 return context.getName(); 084 } 085 086 public String getManagementName() { 087 return context.getManagementName(); 088 } 089 090 public String getCamelVersion() { 091 return context.getVersion(); 092 } 093 094 public String getState() { 095 return context.getStatus().name(); 096 } 097 098 public String getUptime() { 099 return context.getUptime(); 100 } 101 102 public String getClassResolver() { 103 return context.getClassResolver().getClass().getName(); 104 } 105 106 public String getPackageScanClassResolver() { 107 return context.getPackageScanClassResolver().getClass().getName(); 108 } 109 110 public String getApplicationContextClassName() { 111 if (context.getApplicationContextClassLoader() != null) { 112 return context.getApplicationContextClassLoader().toString(); 113 } else { 114 return null; 115 } 116 } 117 118 public Map<String, String> getProperties() { 119 if (context.getProperties().isEmpty()) { 120 return null; 121 } 122 return context.getProperties(); 123 } 124 125 public String getProperty(String name) throws Exception { 126 return context.getProperty(name); 127 } 128 129 public void setProperty(String name, String value) throws Exception { 130 context.getProperties().put(name, value); 131 } 132 133 public Boolean getTracing() { 134 return context.isTracing(); 135 } 136 137 public void setTracing(Boolean tracing) { 138 context.setTracing(tracing); 139 } 140 141 public Integer getInflightExchanges() { 142 return (int) super.getExchangesInflight(); 143 } 144 145 public Integer getTotalRoutes() { 146 return context.getRoutes().size(); 147 } 148 149 public Integer getStartedRoutes() { 150 int started = 0; 151 for (Route route : context.getRoutes()) { 152 if (context.getRouteStatus(route.getId()).isStarted()) { 153 started++; 154 } 155 } 156 return started; 157 } 158 159 public void setTimeout(long timeout) { 160 context.getShutdownStrategy().setTimeout(timeout); 161 } 162 163 public long getTimeout() { 164 return context.getShutdownStrategy().getTimeout(); 165 } 166 167 public void setTimeUnit(TimeUnit timeUnit) { 168 context.getShutdownStrategy().setTimeUnit(timeUnit); 169 } 170 171 public TimeUnit getTimeUnit() { 172 return context.getShutdownStrategy().getTimeUnit(); 173 } 174 175 public void setShutdownNowOnTimeout(boolean shutdownNowOnTimeout) { 176 context.getShutdownStrategy().setShutdownNowOnTimeout(shutdownNowOnTimeout); 177 } 178 179 public boolean isShutdownNowOnTimeout() { 180 return context.getShutdownStrategy().isShutdownNowOnTimeout(); 181 } 182 183 public String getLoad01() { 184 double load1 = load.getLoad1(); 185 if (Double.isNaN(load1)) { 186 // empty string if load statistics is disabled 187 return ""; 188 } else { 189 return String.format("%.2f", load1); 190 } 191 } 192 193 public String getLoad05() { 194 double load5 = load.getLoad5(); 195 if (Double.isNaN(load5)) { 196 // empty string if load statistics is disabled 197 return ""; 198 } else { 199 return String.format("%.2f", load5); 200 } 201 } 202 203 public String getLoad15() { 204 double load15 = load.getLoad15(); 205 if (Double.isNaN(load15)) { 206 // empty string if load statistics is disabled 207 return ""; 208 } else { 209 return String.format("%.2f", load15); 210 } 211 } 212 213 public boolean isUseBreadcrumb() { 214 return context.isUseBreadcrumb(); 215 } 216 217 public boolean isAllowUseOriginalMessage() { 218 return context.isAllowUseOriginalMessage(); 219 } 220 221 public boolean isMessageHistory() { 222 return context.isMessageHistory() != null ? context.isMessageHistory() : false; 223 } 224 225 public boolean isUseMDCLogging() { 226 return context.isUseMDCLogging(); 227 } 228 229 public void onTimer() { 230 load.update(getInflightExchanges()); 231 } 232 233 public void start() throws Exception { 234 if (context.isSuspended()) { 235 context.resume(); 236 } else { 237 context.start(); 238 } 239 } 240 241 public void stop() throws Exception { 242 context.stop(); 243 } 244 245 public void restart() throws Exception { 246 context.stop(); 247 context.start(); 248 } 249 250 public void suspend() throws Exception { 251 context.suspend(); 252 } 253 254 public void resume() throws Exception { 255 if (context.isSuspended()) { 256 context.resume(); 257 } else { 258 throw new IllegalStateException("CamelContext is not suspended"); 259 } 260 } 261 262 public void startAllRoutes() throws Exception { 263 context.startAllRoutes(); 264 } 265 266 public boolean canSendToEndpoint(String endpointUri) { 267 try { 268 Endpoint endpoint = context.getEndpoint(endpointUri); 269 if (endpoint != null) { 270 Producer producer = endpoint.createProducer(); 271 return producer != null; 272 } 273 } catch (Exception e) { 274 // ignore 275 } 276 277 return false; 278 } 279 280 public void sendBody(String endpointUri, Object body) throws Exception { 281 ProducerTemplate template = context.createProducerTemplate(); 282 try { 283 template.sendBody(endpointUri, body); 284 } finally { 285 template.stop(); 286 } 287 } 288 289 public void sendStringBody(String endpointUri, String body) throws Exception { 290 sendBody(endpointUri, body); 291 } 292 293 public void sendBodyAndHeaders(String endpointUri, Object body, Map<String, Object> headers) throws Exception { 294 ProducerTemplate template = context.createProducerTemplate(); 295 try { 296 template.sendBodyAndHeaders(endpointUri, body, headers); 297 } finally { 298 template.stop(); 299 } 300 } 301 302 public Object requestBody(String endpointUri, Object body) throws Exception { 303 ProducerTemplate template = context.createProducerTemplate(); 304 Object answer = null; 305 try { 306 answer = template.requestBody(endpointUri, body); 307 } finally { 308 template.stop(); 309 } 310 return answer; 311 } 312 313 public Object requestStringBody(String endpointUri, String body) throws Exception { 314 return requestBody(endpointUri, body); 315 } 316 317 public Object requestBodyAndHeaders(String endpointUri, Object body, Map<String, Object> headers) throws Exception { 318 ProducerTemplate template = context.createProducerTemplate(); 319 Object answer = null; 320 try { 321 answer = template.requestBodyAndHeaders(endpointUri, body, headers); 322 } finally { 323 template.stop(); 324 } 325 return answer; 326 } 327 328 public String dumpRestsAsXml() throws Exception { 329 List<RestDefinition> rests = context.getRestDefinitions(); 330 if (rests.isEmpty()) { 331 return null; 332 } 333 334 // use a routes definition to dump the rests 335 RestsDefinition def = new RestsDefinition(); 336 def.setRests(rests); 337 return ModelHelper.dumpModelAsXml(context, def); 338 } 339 340 public String dumpRoutesAsXml() throws Exception { 341 List<RouteDefinition> routes = context.getRouteDefinitions(); 342 if (routes.isEmpty()) { 343 return null; 344 } 345 346 // use a routes definition to dump the routes 347 RoutesDefinition def = new RoutesDefinition(); 348 def.setRoutes(routes); 349 return ModelHelper.dumpModelAsXml(context, def); 350 } 351 352 public void addOrUpdateRoutesFromXml(String xml) throws Exception { 353 // do not decode so we function as before 354 addOrUpdateRoutesFromXml(xml, false); 355 } 356 357 public void addOrUpdateRoutesFromXml(String xml, boolean urlDecode) throws Exception { 358 // decode String as it may have been encoded, from its xml source 359 if (urlDecode) { 360 xml = URLDecoder.decode(xml, "UTF-8"); 361 } 362 363 InputStream is = context.getTypeConverter().mandatoryConvertTo(InputStream.class, xml); 364 RoutesDefinition def = context.loadRoutesDefinition(is); 365 if (def == null) { 366 return; 367 } 368 369 // add will remove existing route first 370 context.addRouteDefinitions(def.getRoutes()); 371 } 372 373 public String dumpRoutesStatsAsXml(boolean fullStats, boolean includeProcessors) throws Exception { 374 StringBuilder sb = new StringBuilder(); 375 sb.append("<camelContextStat").append(String.format(" id=\"%s\" state=\"%s\"", getCamelId(), getState())); 376 // use substring as we only want the attributes 377 String stat = dumpStatsAsXml(fullStats); 378 sb.append(" exchangesInflight=\"").append(getInflightExchanges()).append("\""); 379 sb.append(" ").append(stat.substring(7, stat.length() - 2)).append(">\n"); 380 381 MBeanServer server = getContext().getManagementStrategy().getManagementAgent().getMBeanServer(); 382 if (server != null) { 383 // gather all the routes for this CamelContext, which requires JMX 384 String prefix = getContext().getManagementStrategy().getManagementAgent().getIncludeHostName() ? "*/" : ""; 385 ObjectName query = ObjectName.getInstance("org.apache.camel:context=" + prefix + getContext().getManagementName() + ",type=routes,*"); 386 Set<ObjectName> routes = server.queryNames(query, null); 387 388 List<ManagedProcessorMBean> processors = new ArrayList<ManagedProcessorMBean>(); 389 if (includeProcessors) { 390 // gather all the processors for this CamelContext, which requires JMX 391 query = ObjectName.getInstance("org.apache.camel:context=" + prefix + getContext().getManagementName() + ",type=processors,*"); 392 Set<ObjectName> names = server.queryNames(query, null); 393 for (ObjectName on : names) { 394 ManagedProcessorMBean processor = MBeanServerInvocationHandler.newProxyInstance(server, on, ManagedProcessorMBean.class, true); 395 processors.add(processor); 396 } 397 } 398 Collections.sort(processors, new OrderProcessorMBeans()); 399 400 // loop the routes, and append the processor stats if needed 401 sb.append(" <routeStats>\n"); 402 for (ObjectName on : routes) { 403 ManagedRouteMBean route = MBeanServerInvocationHandler.newProxyInstance(server, on, ManagedRouteMBean.class, true); 404 sb.append(" <routeStat").append(String.format(" id=\"%s\" state=\"%s\"", route.getRouteId(), route.getState())); 405 // use substring as we only want the attributes 406 stat = route.dumpStatsAsXml(fullStats); 407 sb.append(" exchangesInflight=\"").append(route.getExchangesInflight()).append("\""); 408 sb.append(" ").append(stat.substring(7, stat.length() - 2)).append(">\n"); 409 410 // add processor details if needed 411 if (includeProcessors) { 412 sb.append(" <processorStats>\n"); 413 for (ManagedProcessorMBean processor : processors) { 414 // the processor must belong to this route 415 if (route.getRouteId().equals(processor.getRouteId())) { 416 sb.append(" <processorStat").append(String.format(" id=\"%s\" index=\"%s\" state=\"%s\"", processor.getProcessorId(), processor.getIndex(), processor.getState())); 417 // use substring as we only want the attributes 418 stat = processor.dumpStatsAsXml(fullStats); 419 sb.append(" exchangesInflight=\"").append(processor.getExchangesInflight()).append("\""); 420 sb.append(" ").append(stat.substring(7)).append("\n"); 421 } 422 } 423 sb.append(" </processorStats>\n"); 424 } 425 sb.append(" </routeStat>\n"); 426 } 427 sb.append(" </routeStats>\n"); 428 } 429 430 sb.append("</camelContextStat>"); 431 return sb.toString(); 432 } 433 434 public boolean createEndpoint(String uri) throws Exception { 435 if (context.hasEndpoint(uri) != null) { 436 // endpoint already exists 437 return false; 438 } 439 440 Endpoint endpoint = context.getEndpoint(uri); 441 if (endpoint != null) { 442 // ensure endpoint is registered, as the management strategy could have been configured to not always 443 // register new endpoints in JMX, so we need to check if its registered, and if not register it manually 444 ObjectName on = context.getManagementStrategy().getManagementNamingStrategy().getObjectNameForEndpoint(endpoint); 445 if (on != null && !context.getManagementStrategy().getManagementAgent().isRegistered(on)) { 446 // register endpoint as mbean 447 Object me = context.getManagementStrategy().getManagementObjectStrategy().getManagedObjectForEndpoint(context, endpoint); 448 context.getManagementStrategy().getManagementAgent().register(me, on); 449 } 450 return true; 451 } else { 452 return false; 453 } 454 } 455 456 public int removeEndpoints(String pattern) throws Exception { 457 // endpoints is always removed from JMX if removed from context 458 Collection<Endpoint> removed = context.removeEndpoints(pattern); 459 return removed.size(); 460 } 461 462 public Map<String, Properties> findEips() throws Exception { 463 return context.findEips(); 464 } 465 466 public List<String> findEipNames() throws Exception { 467 Map<String, Properties> map = findEips(); 468 return new ArrayList<String>(map.keySet()); 469 } 470 471 public TabularData listEips() throws Exception { 472 try { 473 // find all EIPs 474 Map<String, Properties> eips = context.findEips(); 475 476 TabularData answer = new TabularDataSupport(CamelOpenMBeanTypes.listEipsTabularType()); 477 478 // gather EIP detail for each eip 479 for (Map.Entry<String, Properties> entry : eips.entrySet()) { 480 String name = entry.getKey(); 481 String title = (String) entry.getValue().get("title"); 482 String description = (String) entry.getValue().get("description"); 483 String label = (String) entry.getValue().get("label"); 484 String type = (String) entry.getValue().get("class"); 485 String status = CamelContextHelper.isEipInUse(context, name) ? "in use" : "on classpath"; 486 CompositeType ct = CamelOpenMBeanTypes.listEipsCompositeType(); 487 CompositeData data = new CompositeDataSupport(ct, new String[]{"name", "title", "description", "label", "status", "type"}, 488 new Object[]{name, title, description, label, status, type}); 489 answer.put(data); 490 } 491 return answer; 492 } catch (Exception e) { 493 throw ObjectHelper.wrapRuntimeCamelException(e); 494 } 495 } 496 497 public Map<String, Properties> findComponents() throws Exception { 498 Map<String, Properties> answer = context.findComponents(); 499 for (Map.Entry<String, Properties> entry : answer.entrySet()) { 500 if (entry.getValue() != null) { 501 // remove component as its not serializable over JMX 502 entry.getValue().remove("component"); 503 // .. and components which just list all the components in the JAR/bundle and that is verbose and not needed 504 entry.getValue().remove("components"); 505 } 506 } 507 return answer; 508 } 509 510 public String getComponentDocumentation(String componentName) throws IOException { 511 return context.getComponentDocumentation(componentName); 512 } 513 514 public String createRouteStaticEndpointJson() { 515 return createRouteStaticEndpointJson(true); 516 } 517 518 public String createRouteStaticEndpointJson(boolean includeDynamic) { 519 return context.createRouteStaticEndpointJson(null, includeDynamic); 520 } 521 522 public List<String> findComponentNames() throws Exception { 523 Map<String, Properties> map = findComponents(); 524 return new ArrayList<String>(map.keySet()); 525 } 526 527 @Override 528 public TabularData listComponents() throws Exception { 529 try { 530 // find all components 531 Map<String, Properties> components = context.findComponents(); 532 533 TabularData answer = new TabularDataSupport(CamelOpenMBeanTypes.listComponentsTabularType()); 534 535 // gather component detail for each component 536 for (Map.Entry<String, Properties> entry : components.entrySet()) { 537 String name = entry.getKey(); 538 String title = null; 539 String description = null; 540 String label = null; 541 String status = context.hasComponent(name) != null ? "in use" : "on classpath"; 542 String type = (String) entry.getValue().get("class"); 543 String groupId = null; 544 String artifactId = null; 545 String version = null; 546 547 // a component may have been given a different name, so resolve its default name by its java type 548 // as we can find the component json information from the default component name 549 String defaultName = context.resolveComponentDefaultName(type); 550 String target = defaultName != null ? defaultName : name; 551 552 // load component json data, and parse it to gather the component meta-data 553 String json = context.getComponentParameterJsonSchema(target); 554 List<Map<String, String>> rows = JsonSchemaHelper.parseJsonSchema("component", json, false); 555 for (Map<String, String> row : rows) { 556 if (row.containsKey("title")) { 557 title = row.get("title"); 558 } else if (row.containsKey("description")) { 559 description = row.get("description"); 560 } else if (row.containsKey("label")) { 561 label = row.get("label"); 562 } else if (row.containsKey("javaType")) { 563 type = row.get("javaType"); 564 } else if (row.containsKey("groupId")) { 565 groupId = row.get("groupId"); 566 } else if (row.containsKey("artifactId")) { 567 artifactId = row.get("artifactId"); 568 } else if (row.containsKey("version")) { 569 version = row.get("version"); 570 } 571 } 572 573 CompositeType ct = CamelOpenMBeanTypes.listComponentsCompositeType(); 574 CompositeData data = new CompositeDataSupport(ct, new String[]{"name", "title", "description", "label", "status", "type", "groupId", "artifactId", "version"}, 575 new Object[]{name, title, description, label, status, type, groupId, artifactId, version}); 576 answer.put(data); 577 } 578 return answer; 579 } catch (Exception e) { 580 throw ObjectHelper.wrapRuntimeCamelException(e); 581 } 582 } 583 584 public List<String> completeEndpointPath(String componentName, Map<String, Object> endpointParameters, 585 String completionText) throws Exception { 586 if (completionText == null) { 587 completionText = ""; 588 } 589 Component component = context.getComponent(componentName, false); 590 if (component != null) { 591 ComponentConfiguration configuration = component.createComponentConfiguration(); 592 configuration.setParameters(endpointParameters); 593 return configuration.completeEndpointPath(completionText); 594 } else { 595 return new ArrayList<String>(); 596 } 597 } 598 599 public String componentParameterJsonSchema(String componentName) throws Exception { 600 // favor using pre generated schema if component has that 601 String json = context.getComponentParameterJsonSchema(componentName); 602 if (json == null) { 603 // okay this requires having the component on the classpath and being instantiated 604 Component component = context.getComponent(componentName); 605 if (component != null) { 606 ComponentConfiguration configuration = component.createComponentConfiguration(); 607 json = configuration.createParameterJsonSchema(); 608 } 609 } 610 return json; 611 } 612 613 public String dataFormatParameterJsonSchema(String dataFormatName) throws Exception { 614 return context.getDataFormatParameterJsonSchema(dataFormatName); 615 } 616 617 public String languageParameterJsonSchema(String languageName) throws Exception { 618 return context.getLanguageParameterJsonSchema(languageName); 619 } 620 621 public String eipParameterJsonSchema(String eipName) throws Exception { 622 return context.getEipParameterJsonSchema(eipName); 623 } 624 625 public String explainEipJson(String nameOrId, boolean includeAllOptions) { 626 return context.explainEipJson(nameOrId, includeAllOptions); 627 } 628 629 public String explainComponentJson(String componentName, boolean includeAllOptions) throws Exception { 630 return context.explainComponentJson(componentName, includeAllOptions); 631 } 632 633 public String explainEndpointJson(String uri, boolean includeAllOptions) throws Exception { 634 return context.explainEndpointJson(uri, includeAllOptions); 635 } 636 637 public void reset(boolean includeRoutes) throws Exception { 638 reset(); 639 640 // and now reset all routes for this route 641 if (includeRoutes) { 642 MBeanServer server = getContext().getManagementStrategy().getManagementAgent().getMBeanServer(); 643 if (server != null) { 644 String prefix = getContext().getManagementStrategy().getManagementAgent().getIncludeHostName() ? "*/" : ""; 645 ObjectName query = ObjectName.getInstance("org.apache.camel:context=" + prefix + getContext().getManagementName() + ",type=routes,*"); 646 Set<ObjectName> names = server.queryNames(query, null); 647 for (ObjectName name : names) { 648 server.invoke(name, "reset", new Object[]{true}, new String[]{"boolean"}); 649 } 650 } 651 } 652 } 653 654 /** 655 * Used for sorting the processor mbeans accordingly to their index. 656 */ 657 private static final class OrderProcessorMBeans implements Comparator<ManagedProcessorMBean> { 658 659 @Override 660 public int compare(ManagedProcessorMBean o1, ManagedProcessorMBean o2) { 661 return o1.getIndex().compareTo(o2.getIndex()); 662 } 663 } 664 665}