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 Set<String> suspendedBreakpointNodeIds() { 211 return backlogDebugger.getSuspendedBreakpointNodeIds(); 212 } 213 214 @Override 215 public void disableBreakpoint(String nodeId) { 216 backlogDebugger.disableBreakpoint(nodeId); 217 } 218 219 @Override 220 public void enableBreakpoint(String nodeId) { 221 backlogDebugger.enableBreakpoint(nodeId); 222 } 223 224 @Override 225 public int getBodyMaxChars() { 226 return backlogDebugger.getBodyMaxChars(); 227 } 228 229 @Override 230 public void setBodyMaxChars(int bodyMaxChars) { 231 backlogDebugger.setBodyMaxChars(bodyMaxChars); 232 } 233 234 @Override 235 public boolean isIncludeExchangeProperties() { 236 return backlogDebugger.isIncludeExchangeProperties(); 237 } 238 239 @Override 240 public void setIncludeExchangeProperties(boolean includeExchangeProperties) { 241 backlogDebugger.setIncludeExchangeProperties(includeExchangeProperties); 242 } 243 244 @Override 245 public boolean isIncludeExchangeVariables() { 246 return backlogDebugger.isIncludeExchangeVariables(); 247 } 248 249 @Override 250 public void setIncludeExchangeVariables(boolean includeExchangeVariables) { 251 backlogDebugger.setIncludeExchangeVariables(includeExchangeVariables); 252 } 253 254 @Override 255 public boolean isBodyIncludeStreams() { 256 return backlogDebugger.isBodyIncludeStreams(); 257 } 258 259 @Override 260 public void setBodyIncludeStreams(boolean bodyIncludeStreams) { 261 backlogDebugger.setBodyIncludeStreams(bodyIncludeStreams); 262 } 263 264 @Override 265 public boolean isBodyIncludeFiles() { 266 return backlogDebugger.isBodyIncludeFiles(); 267 } 268 269 @Override 270 public void setBodyIncludeFiles(boolean bodyIncludeFiles) { 271 backlogDebugger.setBodyIncludeFiles(bodyIncludeFiles); 272 } 273 274 @Override 275 public String dumpTracedMessagesAsXml(String nodeId) { 276 return backlogDebugger.dumpTracedMessagesAsXml(nodeId); 277 } 278 279 @Override 280 @Deprecated(since = "4.2.0") 281 public String dumpTracedMessagesAsXml(String nodeId, boolean includeExchangeProperties) { 282 return dumpTracedMessagesAsXml(nodeId); 283 } 284 285 @Override 286 public String dumpTracedMessagesAsJSon(String nodeId) { 287 return backlogDebugger.dumpTracedMessagesAsJSon(nodeId); 288 } 289 290 @Override 291 public long getDebugCounter() { 292 return backlogDebugger.getDebugCounter(); 293 } 294 295 @Override 296 public void resetDebugCounter() { 297 backlogDebugger.resetDebugCounter(); 298 } 299 300 @Override 301 public String validateConditionalBreakpoint(String language, String predicate) { 302 Language lan = null; 303 try { 304 lan = camelContext.resolveLanguage(language); 305 lan.createPredicate(predicate); 306 return null; 307 } catch (Exception e) { 308 if (lan == null) { 309 return e.getMessage(); 310 } else { 311 return "Invalid syntax " + predicate + " due: " + e.getMessage(); 312 } 313 } 314 } 315 316 @Override 317 public long getFallbackTimeout() { 318 return backlogDebugger.getFallbackTimeout(); 319 } 320 321 @Override 322 public void setFallbackTimeout(long fallbackTimeout) { 323 backlogDebugger.setFallbackTimeout(fallbackTimeout); 324 } 325 326 @Override 327 public String evaluateExpressionAtBreakpoint(String nodeId, String language, String expression) { 328 return evaluateExpressionAtBreakpoint(nodeId, language, expression, "java.lang.String").toString(); 329 } 330 331 @Override 332 public void setExchangePropertyOnBreakpoint(String nodeId, String exchangePropertyName, Object value) { 333 try { 334 backlogDebugger.setExchangePropertyOnBreakpoint(nodeId, exchangePropertyName, value); 335 } catch (NoTypeConversionAvailableException e) { 336 throw RuntimeCamelException.wrapRuntimeCamelException(e); 337 } 338 } 339 340 @Override 341 public void setExchangePropertyOnBreakpoint(String nodeId, String exchangePropertyName, Object value, String type) { 342 try { 343 Class<?> classType = camelContext.getClassResolver().resolveMandatoryClass(type); 344 backlogDebugger.setExchangePropertyOnBreakpoint(nodeId, exchangePropertyName, value, classType); 345 } catch (Exception e) { 346 throw RuntimeCamelException.wrapRuntimeCamelException(e); 347 } 348 } 349 350 @Override 351 public void removeExchangePropertyOnBreakpoint(String nodeId, String exchangePropertyName) { 352 backlogDebugger.removeExchangePropertyOnBreakpoint(nodeId, exchangePropertyName); 353 } 354 355 @Override 356 public void setExchangeVariableOnBreakpoint(String nodeId, String variableName, Object value) { 357 try { 358 backlogDebugger.setExchangeVariableOnBreakpoint(nodeId, variableName, value); 359 } catch (NoTypeConversionAvailableException e) { 360 throw RuntimeCamelException.wrapRuntimeCamelException(e); 361 } 362 } 363 364 @Override 365 public void setExchangeVariableOnBreakpoint(String nodeId, String variableName, Object value, String type) { 366 try { 367 Class<?> classType = camelContext.getClassResolver().resolveMandatoryClass(type); 368 backlogDebugger.setExchangeVariableOnBreakpoint(nodeId, variableName, value, classType); 369 } catch (Exception e) { 370 throw RuntimeCamelException.wrapRuntimeCamelException(e); 371 } 372 } 373 374 @Override 375 public void removeExchangeVariableOnBreakpoint(String nodeId, String variableName) { 376 backlogDebugger.removeExchangeVariableOnBreakpoint(nodeId, variableName); 377 } 378 379 @Override 380 public Object evaluateExpressionAtBreakpoint(String nodeId, String language, String expression, String resultType) { 381 Exchange suspendedExchange; 382 try { 383 Language lan = camelContext.resolveLanguage(language); 384 suspendedExchange = backlogDebugger.getSuspendedExchange(nodeId); 385 if (suspendedExchange != null) { 386 Object result; 387 Class<?> resultClass = camelContext.getClassResolver().resolveMandatoryClass(resultType); 388 if (!Boolean.class.isAssignableFrom(resultClass)) { 389 Expression expr = lan.createExpression(expression); 390 expr.init(camelContext); 391 result = expr.evaluate(suspendedExchange, resultClass); 392 } else { 393 Predicate pred = lan.createPredicate(expression); 394 pred.init(camelContext); 395 result = pred.matches(suspendedExchange); 396 } 397 //Test if result is serializable 398 if (!isSerializable(result)) { 399 String resultStr = suspendedExchange.getContext().getTypeConverter().tryConvertTo(String.class, result); 400 if (resultStr != null) { 401 result = resultStr; 402 } 403 } 404 return result; 405 } 406 } catch (Exception e) { 407 return e.getMessage(); 408 } 409 return null; 410 } 411 412 @Override 413 public String messageHistoryOnBreakpointAsXml(String nodeId) { 414 StringBuilder messageHistoryBuilder = new StringBuilder(); 415 messageHistoryBuilder.append("<messageHistory>\n"); 416 417 Exchange suspendedExchange = backlogDebugger.getSuspendedExchange(nodeId); 418 if (suspendedExchange != null) { 419 List<MessageHistory> list = suspendedExchange.getProperty(ExchangePropertyKey.MESSAGE_HISTORY, List.class); 420 if (list != null) { 421 // add incoming origin of message on the top 422 String routeId = suspendedExchange.getFromRouteId(); 423 Route route = suspendedExchange.getContext().getRoute(routeId); 424 String loc = route != null ? route.getSourceLocationShort() : ""; 425 String id = routeId; 426 String label = ""; 427 if (suspendedExchange.getFromEndpoint() != null) { 428 label = "from[" 429 + URISupport 430 .sanitizeUri( 431 StringHelper.limitLength(suspendedExchange.getFromEndpoint().getEndpointUri(), 100)) 432 + "]"; 433 } 434 435 long elapsed = suspendedExchange.getClock().elapsed(); 436 437 messageHistoryBuilder 438 .append(" <messageHistoryEntry") 439 .append(" location=\"").append(StringHelper.xmlEncode(loc)).append("\"") 440 .append(" routeId=\"").append(StringHelper.xmlEncode(routeId)).append("\"") 441 .append(" processorId=\"").append(StringHelper.xmlEncode(id)).append("\"") 442 .append(" processor=\"").append(StringHelper.xmlEncode(label)).append("\"") 443 .append(" elapsed=\"").append(elapsed).append("\"") 444 .append("/>\n"); 445 446 for (MessageHistory history : list) { 447 // and then each history 448 loc = LoggerHelper.getLineNumberLoggerName(history.getNode()); 449 if (loc == null) { 450 loc = ""; 451 } 452 routeId = history.getRouteId() != null ? history.getRouteId() : ""; 453 id = history.getNode().getId(); 454 // we need to avoid leak the sensible information here 455 // the sanitizeUri takes a very long time for very long string 456 // and the format cuts this to 457 // 78 characters, anyway. Cut this to 100 characters. This will 458 // give enough space for removing 459 // characters in the sanitizeUri method and will be reasonably 460 // fast 461 label = URISupport.sanitizeUri(StringHelper.limitLength(history.getNode().getLabel(), 100)); 462 elapsed = history.getElapsed(); 463 464 messageHistoryBuilder 465 .append(" <messageHistoryEntry") 466 .append(" location=\"").append(StringHelper.xmlEncode(loc)).append("\"") 467 .append(" routeId=\"").append(StringHelper.xmlEncode(routeId)).append("\"") 468 .append(" processorId=\"").append(StringHelper.xmlEncode(id)).append("\"") 469 .append(" processor=\"").append(StringHelper.xmlEncode(label)).append("\"") 470 .append(" elapsed=\"").append(elapsed).append("\"") 471 .append("/>\n"); 472 } 473 } 474 } 475 messageHistoryBuilder.append("</messageHistory>\n"); 476 return messageHistoryBuilder.toString(); 477 } 478 479 @Override 480 public void attach() { 481 backlogDebugger.attach(); 482 } 483 484 @Override 485 public void detach() { 486 backlogDebugger.detach(); 487 } 488 489 private static boolean isSerializable(Object obj) { 490 final ByteArrayOutputStream baos = new ByteArrayOutputStream(512); 491 try (ObjectOutputStream out = new ObjectOutputStream(baos)) { 492 out.writeObject(obj); 493 return true; 494 } catch (Exception e) { 495 return false; 496 } 497 } 498 499}