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.ByteArrayOutputStream; 020import java.io.ObjectOutputStream; 021import java.util.List; 022import java.util.Set; 023 024import org.apache.camel.CamelContext; 025import org.apache.camel.Exchange; 026import org.apache.camel.ExchangePropertyKey; 027import org.apache.camel.Expression; 028import org.apache.camel.MessageHistory; 029import org.apache.camel.NoTypeConversionAvailableException; 030import org.apache.camel.Predicate; 031import org.apache.camel.Route; 032import org.apache.camel.RuntimeCamelException; 033import org.apache.camel.api.management.ManagedResource; 034import org.apache.camel.api.management.mbean.ManagedBacklogDebuggerMBean; 035import org.apache.camel.impl.debugger.DefaultBacklogDebugger; 036import org.apache.camel.spi.Language; 037import org.apache.camel.spi.ManagementStrategy; 038import org.apache.camel.support.LoggerHelper; 039import org.apache.camel.util.StringHelper; 040import org.apache.camel.util.URISupport; 041 042@ManagedResource(description = "Managed BacklogDebugger") 043public class ManagedBacklogDebugger implements ManagedBacklogDebuggerMBean { 044 045 private final CamelContext camelContext; 046 private final DefaultBacklogDebugger backlogDebugger; 047 048 public ManagedBacklogDebugger(CamelContext camelContext, DefaultBacklogDebugger backlogDebugger) { 049 this.camelContext = camelContext; 050 this.backlogDebugger = backlogDebugger; 051 } 052 053 public void init(ManagementStrategy strategy) { 054 // do nothing 055 } 056 057 public CamelContext getContext() { 058 return camelContext; 059 } 060 061 public DefaultBacklogDebugger getBacklogDebugger() { 062 return backlogDebugger; 063 } 064 065 @Override 066 public String getCamelId() { 067 return camelContext.getName(); 068 } 069 070 @Override 071 public String getCamelManagementName() { 072 return camelContext.getManagementName(); 073 } 074 075 @Override 076 public String getLoggingLevel() { 077 return backlogDebugger.getLoggingLevel(); 078 } 079 080 @Override 081 public void setLoggingLevel(String level) { 082 backlogDebugger.setLoggingLevel(level); 083 } 084 085 @Override 086 public boolean isEnabled() { 087 return backlogDebugger.isEnabled(); 088 } 089 090 @Override 091 public boolean isStandby() { 092 return backlogDebugger.isStandby(); 093 } 094 095 @Override 096 public void enableDebugger() { 097 backlogDebugger.enableDebugger(); 098 } 099 100 @Override 101 public void disableDebugger() { 102 backlogDebugger.disableDebugger(); 103 } 104 105 @Override 106 public void addBreakpoint(String nodeId) { 107 backlogDebugger.addBreakpoint(nodeId); 108 } 109 110 @Override 111 public void addConditionalBreakpoint(String nodeId, String language, String predicate) { 112 backlogDebugger.addConditionalBreakpoint(nodeId, language, predicate); 113 } 114 115 @Override 116 public void removeBreakpoint(String nodeId) { 117 backlogDebugger.removeBreakpoint(nodeId); 118 } 119 120 @Override 121 public void removeAllBreakpoints() { 122 backlogDebugger.removeAllBreakpoints(); 123 } 124 125 @Override 126 public Set<String> breakpoints() { 127 return backlogDebugger.getBreakpoints(); 128 } 129 130 @Override 131 public void resumeBreakpoint(String nodeId) { 132 backlogDebugger.resumeBreakpoint(nodeId); 133 } 134 135 @Override 136 public void setMessageBodyOnBreakpoint(String nodeId, Object body) { 137 backlogDebugger.setMessageBodyOnBreakpoint(nodeId, body); 138 } 139 140 @Override 141 public void setMessageBodyOnBreakpoint(String nodeId, Object body, String type) { 142 try { 143 Class<?> classType = camelContext.getClassResolver().resolveMandatoryClass(type); 144 backlogDebugger.setMessageBodyOnBreakpoint(nodeId, body, classType); 145 } catch (ClassNotFoundException e) { 146 throw RuntimeCamelException.wrapRuntimeCamelException(e); 147 } 148 } 149 150 @Override 151 public void removeMessageBodyOnBreakpoint(String nodeId) { 152 backlogDebugger.removeMessageBodyOnBreakpoint(nodeId); 153 } 154 155 @Override 156 public void setMessageHeaderOnBreakpoint(String nodeId, String headerName, Object value) { 157 try { 158 backlogDebugger.setMessageHeaderOnBreakpoint(nodeId, headerName, value); 159 } catch (NoTypeConversionAvailableException e) { 160 throw RuntimeCamelException.wrapRuntimeCamelException(e); 161 } 162 } 163 164 @Override 165 public void setMessageHeaderOnBreakpoint(String nodeId, String headerName, Object value, String type) { 166 try { 167 Class<?> classType = camelContext.getClassResolver().resolveMandatoryClass(type); 168 backlogDebugger.setMessageHeaderOnBreakpoint(nodeId, headerName, value, classType); 169 } catch (Exception e) { 170 throw RuntimeCamelException.wrapRuntimeCamelException(e); 171 } 172 } 173 174 @Override 175 public void removeMessageHeaderOnBreakpoint(String nodeId, String headerName) { 176 backlogDebugger.removeMessageHeaderOnBreakpoint(nodeId, headerName); 177 } 178 179 @Override 180 public void resumeAll() { 181 backlogDebugger.resumeAll(); 182 } 183 184 @Override 185 public void stepBreakpoint(String nodeId) { 186 backlogDebugger.stepBreakpoint(nodeId); 187 } 188 189 @Override 190 public boolean isSuspendedMode() { 191 return backlogDebugger.isSuspendMode(); 192 } 193 194 @Override 195 public boolean isSingleStepMode() { 196 return backlogDebugger.isSingleStepMode(); 197 } 198 199 @Override 200 public void step() { 201 backlogDebugger.step(); 202 } 203 204 @Override 205 public void stepOver() { 206 backlogDebugger.stepOver(); 207 } 208 209 @Override 210 public void skipOver() { 211 backlogDebugger.skipOver(); 212 } 213 214 @Override 215 public Set<String> suspendedBreakpointNodeIds() { 216 return backlogDebugger.getSuspendedBreakpointNodeIds(); 217 } 218 219 @Override 220 public Set<String> suspendedBreakpointExchangeIds() { 221 return backlogDebugger.getSuspendedExchangeIds(); 222 } 223 224 @Override 225 public void disableBreakpoint(String nodeId) { 226 backlogDebugger.disableBreakpoint(nodeId); 227 } 228 229 @Override 230 public void enableBreakpoint(String nodeId) { 231 backlogDebugger.enableBreakpoint(nodeId); 232 } 233 234 @Override 235 public int getBodyMaxChars() { 236 return backlogDebugger.getBodyMaxChars(); 237 } 238 239 @Override 240 public void setBodyMaxChars(int bodyMaxChars) { 241 backlogDebugger.setBodyMaxChars(bodyMaxChars); 242 } 243 244 @Override 245 public boolean isIncludeExchangeProperties() { 246 return backlogDebugger.isIncludeExchangeProperties(); 247 } 248 249 @Override 250 public void setIncludeExchangeProperties(boolean includeExchangeProperties) { 251 backlogDebugger.setIncludeExchangeProperties(includeExchangeProperties); 252 } 253 254 @Override 255 public boolean isIncludeExchangeVariables() { 256 return backlogDebugger.isIncludeExchangeVariables(); 257 } 258 259 @Override 260 public void setIncludeExchangeVariables(boolean includeExchangeVariables) { 261 backlogDebugger.setIncludeExchangeVariables(includeExchangeVariables); 262 } 263 264 @Override 265 public boolean isBodyIncludeStreams() { 266 return backlogDebugger.isBodyIncludeStreams(); 267 } 268 269 @Override 270 public void setBodyIncludeStreams(boolean bodyIncludeStreams) { 271 backlogDebugger.setBodyIncludeStreams(bodyIncludeStreams); 272 } 273 274 @Override 275 public boolean isBodyIncludeFiles() { 276 return backlogDebugger.isBodyIncludeFiles(); 277 } 278 279 @Override 280 public void setBodyIncludeFiles(boolean bodyIncludeFiles) { 281 backlogDebugger.setBodyIncludeFiles(bodyIncludeFiles); 282 } 283 284 @Override 285 public String dumpTracedMessagesAsXml(String nodeId) { 286 return backlogDebugger.dumpTracedMessagesAsXml(nodeId); 287 } 288 289 @Override 290 @Deprecated(since = "4.2.0") 291 public String dumpTracedMessagesAsXml(String nodeId, boolean includeExchangeProperties) { 292 return dumpTracedMessagesAsXml(nodeId); 293 } 294 295 @Override 296 public String dumpTracedMessagesAsJSon(String nodeId) { 297 return backlogDebugger.dumpTracedMessagesAsJSon(nodeId); 298 } 299 300 @Override 301 public long getDebugCounter() { 302 return backlogDebugger.getDebugCounter(); 303 } 304 305 @Override 306 public void resetDebugCounter() { 307 backlogDebugger.resetDebugCounter(); 308 } 309 310 @Override 311 public String validateConditionalBreakpoint(String language, String predicate) { 312 Language lan = null; 313 try { 314 lan = camelContext.resolveLanguage(language); 315 lan.createPredicate(predicate); 316 return null; 317 } catch (Exception e) { 318 if (lan == null) { 319 return e.getMessage(); 320 } else { 321 return "Invalid syntax " + predicate + " due: " + e.getMessage(); 322 } 323 } 324 } 325 326 @Override 327 public long getFallbackTimeout() { 328 return backlogDebugger.getFallbackTimeout(); 329 } 330 331 @Override 332 public void setFallbackTimeout(long fallbackTimeout) { 333 backlogDebugger.setFallbackTimeout(fallbackTimeout); 334 } 335 336 @Override 337 public String evaluateExpressionAtBreakpoint(String nodeId, String language, String expression) { 338 return evaluateExpressionAtBreakpoint(nodeId, language, expression, "java.lang.String").toString(); 339 } 340 341 @Override 342 public void setExchangePropertyOnBreakpoint(String nodeId, String exchangePropertyName, Object value) { 343 try { 344 backlogDebugger.setExchangePropertyOnBreakpoint(nodeId, exchangePropertyName, value); 345 } catch (NoTypeConversionAvailableException e) { 346 throw RuntimeCamelException.wrapRuntimeCamelException(e); 347 } 348 } 349 350 @Override 351 public void setExchangePropertyOnBreakpoint(String nodeId, String exchangePropertyName, Object value, String type) { 352 try { 353 Class<?> classType = camelContext.getClassResolver().resolveMandatoryClass(type); 354 backlogDebugger.setExchangePropertyOnBreakpoint(nodeId, exchangePropertyName, value, classType); 355 } catch (Exception e) { 356 throw RuntimeCamelException.wrapRuntimeCamelException(e); 357 } 358 } 359 360 @Override 361 public void removeExchangePropertyOnBreakpoint(String nodeId, String exchangePropertyName) { 362 backlogDebugger.removeExchangePropertyOnBreakpoint(nodeId, exchangePropertyName); 363 } 364 365 @Override 366 public void setExchangeVariableOnBreakpoint(String nodeId, String variableName, Object value) { 367 try { 368 backlogDebugger.setExchangeVariableOnBreakpoint(nodeId, variableName, value); 369 } catch (NoTypeConversionAvailableException e) { 370 throw RuntimeCamelException.wrapRuntimeCamelException(e); 371 } 372 } 373 374 @Override 375 public void setExchangeVariableOnBreakpoint(String nodeId, String variableName, Object value, String type) { 376 try { 377 Class<?> classType = camelContext.getClassResolver().resolveMandatoryClass(type); 378 backlogDebugger.setExchangeVariableOnBreakpoint(nodeId, variableName, value, classType); 379 } catch (Exception e) { 380 throw RuntimeCamelException.wrapRuntimeCamelException(e); 381 } 382 } 383 384 @Override 385 public void removeExchangeVariableOnBreakpoint(String nodeId, String variableName) { 386 backlogDebugger.removeExchangeVariableOnBreakpoint(nodeId, variableName); 387 } 388 389 @Override 390 public Object evaluateExpressionAtBreakpoint(String nodeId, String language, String expression, String resultType) { 391 Exchange suspendedExchange; 392 try { 393 Language lan = camelContext.resolveLanguage(language); 394 suspendedExchange = backlogDebugger.getSuspendedExchange(nodeId); 395 if (suspendedExchange != null) { 396 Object result; 397 Class<?> resultClass = camelContext.getClassResolver().resolveMandatoryClass(resultType); 398 if (!Boolean.class.isAssignableFrom(resultClass)) { 399 Expression expr = lan.createExpression(expression); 400 expr.init(camelContext); 401 result = expr.evaluate(suspendedExchange, resultClass); 402 } else { 403 Predicate pred = lan.createPredicate(expression); 404 pred.init(camelContext); 405 result = pred.matches(suspendedExchange); 406 } 407 //Test if result is serializable 408 if (!isSerializable(result)) { 409 String resultStr = suspendedExchange.getContext().getTypeConverter().tryConvertTo(String.class, result); 410 if (resultStr != null) { 411 result = resultStr; 412 } 413 } 414 return result; 415 } 416 } catch (Exception e) { 417 return e.getMessage(); 418 } 419 return null; 420 } 421 422 @Override 423 public String messageHistoryOnBreakpointAsXml(String nodeId) { 424 StringBuilder messageHistoryBuilder = new StringBuilder(); 425 messageHistoryBuilder.append("<messageHistory>\n"); 426 427 Exchange suspendedExchange = backlogDebugger.getSuspendedExchange(nodeId); 428 if (suspendedExchange != null) { 429 List<MessageHistory> list = suspendedExchange.getProperty(ExchangePropertyKey.MESSAGE_HISTORY, List.class); 430 if (list != null) { 431 // add incoming origin of message on the top 432 String routeId = suspendedExchange.getFromRouteId(); 433 Route route = suspendedExchange.getContext().getRoute(routeId); 434 String loc = route != null ? route.getSourceLocationShort() : ""; 435 String id = routeId; 436 String label = ""; 437 if (suspendedExchange.getFromEndpoint() != null) { 438 label = "from[" 439 + URISupport 440 .sanitizeUri( 441 StringHelper.limitLength(suspendedExchange.getFromEndpoint().getEndpointUri(), 100)) 442 + "]"; 443 } 444 445 long elapsed = suspendedExchange.getClock().elapsed(); 446 447 messageHistoryBuilder 448 .append(" <messageHistoryEntry") 449 .append(" location=\"").append(StringHelper.xmlEncode(loc)).append("\"") 450 .append(" routeId=\"").append(StringHelper.xmlEncode(routeId)).append("\"") 451 .append(" processorId=\"").append(StringHelper.xmlEncode(id)).append("\"") 452 .append(" processor=\"").append(StringHelper.xmlEncode(label)).append("\"") 453 .append(" elapsed=\"").append(elapsed).append("\"") 454 .append("/>\n"); 455 456 for (MessageHistory history : list) { 457 // and then each history 458 loc = LoggerHelper.getLineNumberLoggerName(history.getNode()); 459 if (loc == null) { 460 loc = ""; 461 } 462 routeId = history.getRouteId() != null ? history.getRouteId() : ""; 463 id = history.getNode().getId(); 464 // we need to avoid leak the sensible information here 465 // the sanitizeUri takes a very long time for very long string 466 // and the format cuts this to 467 // 78 characters, anyway. Cut this to 100 characters. This will 468 // give enough space for removing 469 // characters in the sanitizeUri method and will be reasonably 470 // fast 471 label = URISupport.sanitizeUri(StringHelper.limitLength(history.getNode().getLabel(), 100)); 472 elapsed = history.getElapsed(); 473 474 messageHistoryBuilder 475 .append(" <messageHistoryEntry") 476 .append(" location=\"").append(StringHelper.xmlEncode(loc)).append("\"") 477 .append(" routeId=\"").append(StringHelper.xmlEncode(routeId)).append("\"") 478 .append(" processorId=\"").append(StringHelper.xmlEncode(id)).append("\"") 479 .append(" processor=\"").append(StringHelper.xmlEncode(label)).append("\"") 480 .append(" elapsed=\"").append(elapsed).append("\"") 481 .append("/>\n"); 482 } 483 } 484 } 485 messageHistoryBuilder.append("</messageHistory>\n"); 486 return messageHistoryBuilder.toString(); 487 } 488 489 @Override 490 public void attach() { 491 backlogDebugger.attach(); 492 } 493 494 @Override 495 public void detach() { 496 backlogDebugger.detach(); 497 } 498 499 private static boolean isSerializable(Object obj) { 500 final ByteArrayOutputStream baos = new ByteArrayOutputStream(512); 501 try (ObjectOutputStream out = new ObjectOutputStream(baos)) { 502 out.writeObject(obj); 503 return true; 504 } catch (Exception e) { 505 return false; 506 } 507 } 508 509}