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