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 */ 017 package org.apache.camel.management.mbean; 018 019 import java.io.IOException; 020 import java.io.InputStream; 021 import java.net.URLDecoder; 022 import java.util.ArrayList; 023 import java.util.Collection; 024 import java.util.LinkedHashSet; 025 import java.util.List; 026 import java.util.Map; 027 import java.util.Properties; 028 import java.util.Set; 029 import java.util.concurrent.TimeUnit; 030 import javax.management.MBeanServer; 031 import javax.management.MBeanServerInvocationHandler; 032 import javax.management.ObjectName; 033 034 import org.apache.camel.CamelContext; 035 import org.apache.camel.Component; 036 import org.apache.camel.ComponentConfiguration; 037 import org.apache.camel.Endpoint; 038 import org.apache.camel.ManagementStatisticsLevel; 039 import org.apache.camel.ProducerTemplate; 040 import org.apache.camel.Route; 041 import org.apache.camel.TimerListener; 042 import org.apache.camel.api.management.ManagedResource; 043 import org.apache.camel.api.management.mbean.ManagedCamelContextMBean; 044 import org.apache.camel.api.management.mbean.ManagedProcessorMBean; 045 import org.apache.camel.api.management.mbean.ManagedRouteMBean; 046 import org.apache.camel.model.ModelCamelContext; 047 import org.apache.camel.model.ModelHelper; 048 import org.apache.camel.model.RouteDefinition; 049 import org.apache.camel.model.RoutesDefinition; 050 051 /** 052 * @version 053 */ 054 @ManagedResource(description = "Managed CamelContext") 055 public class ManagedCamelContext extends ManagedPerformanceCounter implements TimerListener, ManagedCamelContextMBean { 056 private final ModelCamelContext context; 057 private final LoadTriplet load = new LoadTriplet(); 058 059 public ManagedCamelContext(ModelCamelContext context) { 060 this.context = context; 061 boolean enabled = context.getManagementStrategy().getStatisticsLevel() != ManagementStatisticsLevel.Off; 062 setStatisticsEnabled(enabled); 063 } 064 065 public CamelContext getContext() { 066 return context; 067 } 068 069 public String getCamelId() { 070 return context.getName(); 071 } 072 073 public String getManagementName() { 074 return context.getManagementName(); 075 } 076 077 public String getCamelVersion() { 078 return context.getVersion(); 079 } 080 081 public String getState() { 082 return context.getStatus().name(); 083 } 084 085 public String getUptime() { 086 return context.getUptime(); 087 } 088 089 public String getClassResolver() { 090 return context.getClassResolver().getClass().getName(); 091 } 092 093 public String getPackageScanClassResolver() { 094 return context.getPackageScanClassResolver().getClass().getName(); 095 } 096 097 public String getApplicationContextClassName() { 098 if (context.getApplicationContextClassLoader() != null) { 099 return context.getApplicationContextClassLoader().toString(); 100 } else { 101 return null; 102 } 103 } 104 105 public Map<String, String> getProperties() { 106 if (context.getProperties().isEmpty()) { 107 return null; 108 } 109 return context.getProperties(); 110 } 111 112 public String getProperty(String name) throws Exception { 113 return context.getProperty(name); 114 } 115 116 public void setProperty(String name, String value) throws Exception { 117 context.getProperties().put(name, value); 118 } 119 120 public Boolean getTracing() { 121 return context.isTracing(); 122 } 123 124 public void setTracing(Boolean tracing) { 125 context.setTracing(tracing); 126 } 127 128 public Boolean getMessageHistory() { 129 return context.isMessageHistory(); 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(); 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 sendBody(String endpointUri, Object body) throws Exception { 254 ProducerTemplate template = context.createProducerTemplate(); 255 try { 256 template.sendBody(endpointUri, body); 257 } finally { 258 template.stop(); 259 } 260 } 261 262 public void sendStringBody(String endpointUri, String body) throws Exception { 263 sendBody(endpointUri, body); 264 } 265 266 public void sendBodyAndHeaders(String endpointUri, Object body, Map<String, Object> headers) throws Exception { 267 ProducerTemplate template = context.createProducerTemplate(); 268 try { 269 template.sendBodyAndHeaders(endpointUri, body, headers); 270 } finally { 271 template.stop(); 272 } 273 } 274 275 public Object requestBody(String endpointUri, Object body) throws Exception { 276 ProducerTemplate template = context.createProducerTemplate(); 277 Object answer = null; 278 try { 279 answer = template.requestBody(endpointUri, body); 280 } finally { 281 template.stop(); 282 } 283 return answer; 284 } 285 286 public Object requestStringBody(String endpointUri, String body) throws Exception { 287 return requestBody(endpointUri, body); 288 } 289 290 public Object requestBodyAndHeaders(String endpointUri, Object body, Map<String, Object> headers) throws Exception { 291 ProducerTemplate template = context.createProducerTemplate(); 292 Object answer = null; 293 try { 294 answer = template.requestBodyAndHeaders(endpointUri, body, headers); 295 } finally { 296 template.stop(); 297 } 298 return answer; 299 } 300 301 public String dumpRoutesAsXml() throws Exception { 302 List<RouteDefinition> routes = context.getRouteDefinitions(); 303 if (routes.isEmpty()) { 304 return null; 305 } 306 307 // use a routes definition to dump the routes 308 RoutesDefinition def = new RoutesDefinition(); 309 def.setRoutes(routes); 310 return ModelHelper.dumpModelAsXml(def); 311 } 312 313 public void addOrUpdateRoutesFromXml(String xml) throws Exception { 314 // do not decode so we function as before 315 addOrUpdateRoutesFromXml(xml, false); 316 } 317 318 public void addOrUpdateRoutesFromXml(String xml, boolean urlDecode) throws Exception { 319 // decode String as it may have been encoded, from its xml source 320 if (urlDecode) { 321 xml = URLDecoder.decode(xml, "UTF-8"); 322 } 323 324 InputStream is = context.getTypeConverter().mandatoryConvertTo(InputStream.class, xml); 325 RoutesDefinition def = context.loadRoutesDefinition(is); 326 if (def == null) { 327 return; 328 } 329 330 // add will remove existing route first 331 context.addRouteDefinitions(def.getRoutes()); 332 } 333 334 public String dumpRoutesStatsAsXml(boolean fullStats, boolean includeProcessors) throws Exception { 335 StringBuilder sb = new StringBuilder(); 336 sb.append("<camelContextStat").append(String.format(" id=\"%s\"", getCamelId())); 337 // use substring as we only want the attributes 338 String stat = dumpStatsAsXml(fullStats); 339 sb.append(" ").append(stat.substring(7, stat.length() - 2)).append(">\n"); 340 341 MBeanServer server = getContext().getManagementStrategy().getManagementAgent().getMBeanServer(); 342 if (server != null) { 343 // gather all the routes for this CamelContext, which requires JMX 344 String prefix = getContext().getManagementStrategy().getManagementAgent().getIncludeHostName() ? "*/" : ""; 345 ObjectName query = ObjectName.getInstance("org.apache.camel:context=" + prefix + getContext().getManagementName() + ",type=routes,*"); 346 Set<ObjectName> routes = server.queryNames(query, null); 347 348 Set<ManagedProcessorMBean> processors = new LinkedHashSet<ManagedProcessorMBean>(); 349 if (includeProcessors) { 350 // gather all the processors for this CamelContext, which requires JMX 351 query = ObjectName.getInstance("org.apache.camel:context=" + prefix + getContext().getManagementName() + ",type=processors,*"); 352 Set<ObjectName> names = server.queryNames(query, null); 353 for (ObjectName on : names) { 354 ManagedProcessorMBean processor = MBeanServerInvocationHandler.newProxyInstance(server, on, ManagedProcessorMBean.class, true); 355 processors.add(processor); 356 } 357 } 358 359 // loop the routes, and append the processor stats if needed 360 sb.append(" <routeStats>\n"); 361 for (ObjectName on : routes) { 362 ManagedRouteMBean route = MBeanServerInvocationHandler.newProxyInstance(server, on, ManagedRouteMBean.class, true); 363 sb.append(" <routeStat").append(String.format(" id=\"%s\"", route.getRouteId())); 364 // use substring as we only want the attributes 365 stat = route.dumpStatsAsXml(fullStats); 366 sb.append(" ").append(stat.substring(7, stat.length() - 2)).append(">\n"); 367 368 // add processor details if needed 369 if (includeProcessors) { 370 sb.append(" <processorStats>\n"); 371 for (ManagedProcessorMBean processor : processors) { 372 // the processor must belong to this route 373 if (route.getRouteId().equals(processor.getRouteId())) { 374 sb.append(" <processorStat").append(String.format(" id=\"%s\"", processor.getProcessorId())); 375 // use substring as we only want the attributes 376 sb.append(" ").append(processor.dumpStatsAsXml(fullStats).substring(7)).append("\n"); 377 } 378 } 379 sb.append(" </processorStats>\n"); 380 } 381 sb.append(" </routeStat>\n"); 382 } 383 sb.append(" </routeStats>\n"); 384 } 385 386 sb.append("</camelContextStat>"); 387 return sb.toString(); 388 } 389 390 public boolean createEndpoint(String uri) throws Exception { 391 if (context.hasEndpoint(uri) != null) { 392 // endpoint already exists 393 return false; 394 } 395 396 Endpoint endpoint = context.getEndpoint(uri); 397 if (endpoint != null) { 398 // ensure endpoint is registered, as the management strategy could have been configured to not always 399 // register new endpoints in JMX, so we need to check if its registered, and if not register it manually 400 ObjectName on = context.getManagementStrategy().getManagementNamingStrategy().getObjectNameForEndpoint(endpoint); 401 if (on != null && !context.getManagementStrategy().getManagementAgent().isRegistered(on)) { 402 // register endpoint as mbean 403 Object me = context.getManagementStrategy().getManagementObjectStrategy().getManagedObjectForEndpoint(context, endpoint); 404 context.getManagementStrategy().getManagementAgent().register(me, on); 405 } 406 return true; 407 } else { 408 return false; 409 } 410 } 411 412 public int removeEndpoints(String pattern) throws Exception { 413 // endpoints is always removed from JMX if removed from context 414 Collection<Endpoint> removed = context.removeEndpoints(pattern); 415 return removed.size(); 416 } 417 418 public Map<String, Properties> findComponents() throws Exception { 419 Map<String, Properties> answer = context.findComponents(); 420 for (Map.Entry<String, Properties> entry : answer.entrySet()) { 421 if (entry.getValue() != null) { 422 // remove component as its not serializable over JMX 423 entry.getValue().remove("component"); 424 // .. and components which just list all the components in the JAR/bundle and that is verbose and not needed 425 entry.getValue().remove("components"); 426 } 427 } 428 return answer; 429 } 430 431 public String getComponentDocumentation(String componentName) throws IOException { 432 return context.getComponentDocumentation(componentName); 433 } 434 435 public String createRouteStaticEndpointJson() { 436 return context.createRouteStaticEndpointJson(null); 437 } 438 439 public List<String> findComponentNames() throws Exception { 440 Map<String, Properties> map = findComponents(); 441 return new ArrayList<String>(map.keySet()); 442 } 443 444 public List<String> completeEndpointPath(String componentName, Map<String, Object> endpointParameters, 445 String completionText) throws Exception { 446 if (completionText == null) { 447 completionText = ""; 448 } 449 Component component = context.getComponent(componentName, false); 450 if (component != null) { 451 ComponentConfiguration configuration = component.createComponentConfiguration(); 452 configuration.setParameters(endpointParameters); 453 return configuration.completeEndpointPath(completionText); 454 } else { 455 return new ArrayList<String>(); 456 } 457 } 458 459 public String componentParameterJsonSchema(String componentName) throws Exception { 460 Component component = context.getComponent(componentName); 461 if (component != null) { 462 ComponentConfiguration configuration = component.createComponentConfiguration(); 463 return configuration.createParameterJsonSchema(); 464 } else { 465 return null; 466 } 467 } 468 469 public void reset(boolean includeRoutes) throws Exception { 470 reset(); 471 472 // and now reset all routes for this route 473 if (includeRoutes) { 474 MBeanServer server = getContext().getManagementStrategy().getManagementAgent().getMBeanServer(); 475 if (server != null) { 476 String prefix = getContext().getManagementStrategy().getManagementAgent().getIncludeHostName() ? "*/" : ""; 477 ObjectName query = ObjectName.getInstance("org.apache.camel:context=" + prefix + getContext().getManagementName() + ",type=routes,*"); 478 Set<ObjectName> names = server.queryNames(query, null); 479 for (ObjectName name : names) { 480 server.invoke(name, "reset", new Object[]{true}, new String[]{"boolean"}); 481 } 482 } 483 } 484 } 485 486 }