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