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; 034 035import org.apache.camel.CamelContext; 036import org.apache.camel.Component; 037import org.apache.camel.ComponentConfiguration; 038import org.apache.camel.Endpoint; 039import org.apache.camel.ManagementStatisticsLevel; 040import org.apache.camel.Producer; 041import org.apache.camel.ProducerTemplate; 042import org.apache.camel.Route; 043import org.apache.camel.TimerListener; 044import org.apache.camel.api.management.ManagedResource; 045import org.apache.camel.api.management.mbean.ManagedCamelContextMBean; 046import org.apache.camel.api.management.mbean.ManagedProcessorMBean; 047import org.apache.camel.api.management.mbean.ManagedRouteMBean; 048import org.apache.camel.model.ModelCamelContext; 049import org.apache.camel.model.ModelHelper; 050import org.apache.camel.model.RouteDefinition; 051import org.apache.camel.model.RoutesDefinition; 052import org.apache.camel.model.rest.RestDefinition; 053import org.apache.camel.model.rest.RestsDefinition; 054 055/** 056 * @version 057 */ 058@ManagedResource(description = "Managed CamelContext") 059public class ManagedCamelContext extends ManagedPerformanceCounter implements TimerListener, ManagedCamelContextMBean { 060 private final ModelCamelContext context; 061 private final LoadTriplet load = new LoadTriplet(); 062 063 public ManagedCamelContext(ModelCamelContext context) { 064 this.context = context; 065 boolean enabled = context.getManagementStrategy().getStatisticsLevel() != ManagementStatisticsLevel.Off; 066 setStatisticsEnabled(enabled); 067 } 068 069 public CamelContext getContext() { 070 return context; 071 } 072 073 public String getCamelId() { 074 return context.getName(); 075 } 076 077 public String getManagementName() { 078 return context.getManagementName(); 079 } 080 081 public String getCamelVersion() { 082 return context.getVersion(); 083 } 084 085 public String getState() { 086 return context.getStatus().name(); 087 } 088 089 public String getUptime() { 090 return context.getUptime(); 091 } 092 093 public String getClassResolver() { 094 return context.getClassResolver().getClass().getName(); 095 } 096 097 public String getPackageScanClassResolver() { 098 return context.getPackageScanClassResolver().getClass().getName(); 099 } 100 101 public String getApplicationContextClassName() { 102 if (context.getApplicationContextClassLoader() != null) { 103 return context.getApplicationContextClassLoader().toString(); 104 } else { 105 return null; 106 } 107 } 108 109 public Map<String, String> getProperties() { 110 if (context.getProperties().isEmpty()) { 111 return null; 112 } 113 return context.getProperties(); 114 } 115 116 public String getProperty(String name) throws Exception { 117 return context.getProperty(name); 118 } 119 120 public void setProperty(String name, String value) throws Exception { 121 context.getProperties().put(name, value); 122 } 123 124 public Boolean getTracing() { 125 return context.isTracing(); 126 } 127 128 public void setTracing(Boolean tracing) { 129 context.setTracing(tracing); 130 } 131 132 public Integer getInflightExchanges() { 133 return context.getInflightRepository().size(); 134 } 135 136 public Integer getTotalRoutes() { 137 return context.getRoutes().size(); 138 } 139 140 public Integer getStartedRoutes() { 141 int started = 0; 142 for (Route route : context.getRoutes()) { 143 if (context.getRouteStatus(route.getId()).isStarted()) { 144 started++; 145 } 146 } 147 return started; 148 } 149 150 public void setTimeout(long timeout) { 151 context.getShutdownStrategy().setTimeout(timeout); 152 } 153 154 public long getTimeout() { 155 return context.getShutdownStrategy().getTimeout(); 156 } 157 158 public void setTimeUnit(TimeUnit timeUnit) { 159 context.getShutdownStrategy().setTimeUnit(timeUnit); 160 } 161 162 public TimeUnit getTimeUnit() { 163 return context.getShutdownStrategy().getTimeUnit(); 164 } 165 166 public void setShutdownNowOnTimeout(boolean shutdownNowOnTimeout) { 167 context.getShutdownStrategy().setShutdownNowOnTimeout(shutdownNowOnTimeout); 168 } 169 170 public boolean isShutdownNowOnTimeout() { 171 return context.getShutdownStrategy().isShutdownNowOnTimeout(); 172 } 173 174 public String getLoad01() { 175 double load1 = load.getLoad1(); 176 if (Double.isNaN(load1)) { 177 // empty string if load statistics is disabled 178 return ""; 179 } else { 180 return String.format("%.2f", load1); 181 } 182 } 183 184 public String getLoad05() { 185 double load5 = load.getLoad5(); 186 if (Double.isNaN(load5)) { 187 // empty string if load statistics is disabled 188 return ""; 189 } else { 190 return String.format("%.2f", load5); 191 } 192 } 193 194 public String getLoad15() { 195 double load15 = load.getLoad15(); 196 if (Double.isNaN(load15)) { 197 // empty string if load statistics is disabled 198 return ""; 199 } else { 200 return String.format("%.2f", load15); 201 } 202 } 203 204 public boolean isUseBreadcrumb() { 205 return context.isUseBreadcrumb(); 206 } 207 208 public boolean isAllowUseOriginalMessage() { 209 return context.isAllowUseOriginalMessage(); 210 } 211 212 public boolean isMessageHistory() { 213 return context.isMessageHistory() != null ? context.isMessageHistory() : false; 214 } 215 216 public boolean isUseMDCLogging() { 217 return context.isUseMDCLogging(); 218 } 219 220 public void onTimer() { 221 load.update(getInflightExchanges()); 222 } 223 224 public void start() throws Exception { 225 if (context.isSuspended()) { 226 context.resume(); 227 } else { 228 context.start(); 229 } 230 } 231 232 public void stop() throws Exception { 233 context.stop(); 234 } 235 236 public void restart() throws Exception { 237 context.stop(); 238 context.start(); 239 } 240 241 public void suspend() throws Exception { 242 context.suspend(); 243 } 244 245 public void resume() throws Exception { 246 if (context.isSuspended()) { 247 context.resume(); 248 } else { 249 throw new IllegalStateException("CamelContext is not suspended"); 250 } 251 } 252 253 public void startAllRoutes() throws Exception { 254 context.startAllRoutes(); 255 } 256 257 public boolean canSendToEndpoint(String endpointUri) { 258 try { 259 Endpoint endpoint = context.getEndpoint(endpointUri); 260 if (endpoint != null) { 261 Producer producer = endpoint.createProducer(); 262 return producer != null; 263 } 264 } catch (Exception e) { 265 // ignore 266 } 267 268 return false; 269 } 270 271 public void sendBody(String endpointUri, Object body) throws Exception { 272 ProducerTemplate template = context.createProducerTemplate(); 273 try { 274 template.sendBody(endpointUri, body); 275 } finally { 276 template.stop(); 277 } 278 } 279 280 public void sendStringBody(String endpointUri, String body) throws Exception { 281 sendBody(endpointUri, body); 282 } 283 284 public void sendBodyAndHeaders(String endpointUri, Object body, Map<String, Object> headers) throws Exception { 285 ProducerTemplate template = context.createProducerTemplate(); 286 try { 287 template.sendBodyAndHeaders(endpointUri, body, headers); 288 } finally { 289 template.stop(); 290 } 291 } 292 293 public Object requestBody(String endpointUri, Object body) throws Exception { 294 ProducerTemplate template = context.createProducerTemplate(); 295 Object answer = null; 296 try { 297 answer = template.requestBody(endpointUri, body); 298 } finally { 299 template.stop(); 300 } 301 return answer; 302 } 303 304 public Object requestStringBody(String endpointUri, String body) throws Exception { 305 return requestBody(endpointUri, body); 306 } 307 308 public Object requestBodyAndHeaders(String endpointUri, Object body, Map<String, Object> headers) throws Exception { 309 ProducerTemplate template = context.createProducerTemplate(); 310 Object answer = null; 311 try { 312 answer = template.requestBodyAndHeaders(endpointUri, body, headers); 313 } finally { 314 template.stop(); 315 } 316 return answer; 317 } 318 319 public String dumpRestsAsXml() throws Exception { 320 List<RestDefinition> rests = context.getRestDefinitions(); 321 if (rests.isEmpty()) { 322 return null; 323 } 324 325 // use a routes definition to dump the rests 326 RestsDefinition def = new RestsDefinition(); 327 def.setRests(rests); 328 return ModelHelper.dumpModelAsXml(def); 329 } 330 331 public String dumpRoutesAsXml() throws Exception { 332 List<RouteDefinition> routes = context.getRouteDefinitions(); 333 if (routes.isEmpty()) { 334 return null; 335 } 336 337 // use a routes definition to dump the routes 338 RoutesDefinition def = new RoutesDefinition(); 339 def.setRoutes(routes); 340 return ModelHelper.dumpModelAsXml(def); 341 } 342 343 public void addOrUpdateRoutesFromXml(String xml) throws Exception { 344 // do not decode so we function as before 345 addOrUpdateRoutesFromXml(xml, false); 346 } 347 348 public void addOrUpdateRoutesFromXml(String xml, boolean urlDecode) throws Exception { 349 // decode String as it may have been encoded, from its xml source 350 if (urlDecode) { 351 xml = URLDecoder.decode(xml, "UTF-8"); 352 } 353 354 InputStream is = context.getTypeConverter().mandatoryConvertTo(InputStream.class, xml); 355 RoutesDefinition def = context.loadRoutesDefinition(is); 356 if (def == null) { 357 return; 358 } 359 360 // add will remove existing route first 361 context.addRouteDefinitions(def.getRoutes()); 362 } 363 364 public String dumpRoutesStatsAsXml(boolean fullStats, boolean includeProcessors) throws Exception { 365 StringBuilder sb = new StringBuilder(); 366 sb.append("<camelContextStat").append(String.format(" id=\"%s\"", getCamelId())); 367 // use substring as we only want the attributes 368 String stat = dumpStatsAsXml(fullStats); 369 sb.append(" ").append(stat.substring(7, stat.length() - 2)).append(">\n"); 370 371 MBeanServer server = getContext().getManagementStrategy().getManagementAgent().getMBeanServer(); 372 if (server != null) { 373 // gather all the routes for this CamelContext, which requires JMX 374 String prefix = getContext().getManagementStrategy().getManagementAgent().getIncludeHostName() ? "*/" : ""; 375 ObjectName query = ObjectName.getInstance("org.apache.camel:context=" + prefix + getContext().getManagementName() + ",type=routes,*"); 376 Set<ObjectName> routes = server.queryNames(query, null); 377 378 List<ManagedProcessorMBean> processors = new ArrayList<ManagedProcessorMBean>(); 379 if (includeProcessors) { 380 // gather all the processors for this CamelContext, which requires JMX 381 query = ObjectName.getInstance("org.apache.camel:context=" + prefix + getContext().getManagementName() + ",type=processors,*"); 382 Set<ObjectName> names = server.queryNames(query, null); 383 for (ObjectName on : names) { 384 ManagedProcessorMBean processor = MBeanServerInvocationHandler.newProxyInstance(server, on, ManagedProcessorMBean.class, true); 385 processors.add(processor); 386 } 387 } 388 Collections.sort(processors, new OrderProcessorMBeans()); 389 390 // loop the routes, and append the processor stats if needed 391 sb.append(" <routeStats>\n"); 392 for (ObjectName on : routes) { 393 ManagedRouteMBean route = MBeanServerInvocationHandler.newProxyInstance(server, on, ManagedRouteMBean.class, true); 394 sb.append(" <routeStat").append(String.format(" id=\"%s\"", route.getRouteId())); 395 // use substring as we only want the attributes 396 stat = route.dumpStatsAsXml(fullStats); 397 sb.append(" ").append(stat.substring(7, stat.length() - 2)).append(">\n"); 398 399 // add processor details if needed 400 if (includeProcessors) { 401 sb.append(" <processorStats>\n"); 402 for (ManagedProcessorMBean processor : processors) { 403 // the processor must belong to this route 404 if (route.getRouteId().equals(processor.getRouteId())) { 405 sb.append(" <processorStat").append(String.format(" id=\"%s\"", processor.getProcessorId())); 406 // use substring as we only want the attributes 407 sb.append(" ").append(processor.dumpStatsAsXml(fullStats).substring(7)).append("\n"); 408 } 409 } 410 sb.append(" </processorStats>\n"); 411 } 412 sb.append(" </routeStat>\n"); 413 } 414 sb.append(" </routeStats>\n"); 415 } 416 417 sb.append("</camelContextStat>"); 418 return sb.toString(); 419 } 420 421 public boolean createEndpoint(String uri) throws Exception { 422 if (context.hasEndpoint(uri) != null) { 423 // endpoint already exists 424 return false; 425 } 426 427 Endpoint endpoint = context.getEndpoint(uri); 428 if (endpoint != null) { 429 // ensure endpoint is registered, as the management strategy could have been configured to not always 430 // register new endpoints in JMX, so we need to check if its registered, and if not register it manually 431 ObjectName on = context.getManagementStrategy().getManagementNamingStrategy().getObjectNameForEndpoint(endpoint); 432 if (on != null && !context.getManagementStrategy().getManagementAgent().isRegistered(on)) { 433 // register endpoint as mbean 434 Object me = context.getManagementStrategy().getManagementObjectStrategy().getManagedObjectForEndpoint(context, endpoint); 435 context.getManagementStrategy().getManagementAgent().register(me, on); 436 } 437 return true; 438 } else { 439 return false; 440 } 441 } 442 443 public int removeEndpoints(String pattern) throws Exception { 444 // endpoints is always removed from JMX if removed from context 445 Collection<Endpoint> removed = context.removeEndpoints(pattern); 446 return removed.size(); 447 } 448 449 public Map<String, Properties> findComponents() throws Exception { 450 Map<String, Properties> answer = context.findComponents(); 451 for (Map.Entry<String, Properties> entry : answer.entrySet()) { 452 if (entry.getValue() != null) { 453 // remove component as its not serializable over JMX 454 entry.getValue().remove("component"); 455 // .. and components which just list all the components in the JAR/bundle and that is verbose and not needed 456 entry.getValue().remove("components"); 457 } 458 } 459 return answer; 460 } 461 462 public String getComponentDocumentation(String componentName) throws IOException { 463 return context.getComponentDocumentation(componentName); 464 } 465 466 public String createRouteStaticEndpointJson() { 467 return createRouteStaticEndpointJson(true); 468 } 469 470 public String createRouteStaticEndpointJson(boolean includeDynamic) { 471 return context.createRouteStaticEndpointJson(null, includeDynamic); 472 } 473 474 public List<String> findComponentNames() throws Exception { 475 Map<String, Properties> map = findComponents(); 476 return new ArrayList<String>(map.keySet()); 477 } 478 479 public List<String> completeEndpointPath(String componentName, Map<String, Object> endpointParameters, 480 String completionText) throws Exception { 481 if (completionText == null) { 482 completionText = ""; 483 } 484 Component component = context.getComponent(componentName, false); 485 if (component != null) { 486 ComponentConfiguration configuration = component.createComponentConfiguration(); 487 configuration.setParameters(endpointParameters); 488 return configuration.completeEndpointPath(completionText); 489 } else { 490 return new ArrayList<String>(); 491 } 492 } 493 494 public String componentParameterJsonSchema(String componentName) throws Exception { 495 Component component = context.getComponent(componentName); 496 if (component != null) { 497 ComponentConfiguration configuration = component.createComponentConfiguration(); 498 return configuration.createParameterJsonSchema(); 499 } else { 500 return null; 501 } 502 } 503 504 public void reset(boolean includeRoutes) throws Exception { 505 reset(); 506 507 // and now reset all routes for this route 508 if (includeRoutes) { 509 MBeanServer server = getContext().getManagementStrategy().getManagementAgent().getMBeanServer(); 510 if (server != null) { 511 String prefix = getContext().getManagementStrategy().getManagementAgent().getIncludeHostName() ? "*/" : ""; 512 ObjectName query = ObjectName.getInstance("org.apache.camel:context=" + prefix + getContext().getManagementName() + ",type=routes,*"); 513 Set<ObjectName> names = server.queryNames(query, null); 514 for (ObjectName name : names) { 515 server.invoke(name, "reset", new Object[]{true}, new String[]{"boolean"}); 516 } 517 } 518 } 519 } 520 521 /** 522 * Used for sorting the processor mbeans accordingly to their index. 523 */ 524 private static final class OrderProcessorMBeans implements Comparator<ManagedProcessorMBean> { 525 526 @Override 527 public int compare(ManagedProcessorMBean o1, ManagedProcessorMBean o2) { 528 return o1.getIndex().compareTo(o2.getIndex()); 529 } 530 } 531 532}