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.model; 018 019import java.io.UnsupportedEncodingException; 020import java.net.URISyntaxException; 021import java.util.ArrayList; 022import java.util.HashMap; 023import java.util.HashSet; 024import java.util.Iterator; 025import java.util.LinkedHashSet; 026import java.util.List; 027import java.util.Map; 028import java.util.Set; 029 030import org.apache.camel.CamelContext; 031import org.apache.camel.ErrorHandlerFactory; 032import org.apache.camel.ExtendedCamelContext; 033import org.apache.camel.RuntimeCamelException; 034import org.apache.camel.builder.ErrorHandlerBuilder; 035import org.apache.camel.model.rest.RestDefinition; 036import org.apache.camel.model.rest.VerbDefinition; 037import org.apache.camel.support.CamelContextHelper; 038import org.apache.camel.support.EndpointHelper; 039import org.apache.camel.util.ObjectHelper; 040import org.apache.camel.util.URISupport; 041 042import static org.apache.camel.model.ProcessorDefinitionHelper.filterTypeInOutputs; 043 044/** 045 * Helper for {@link RouteDefinition} 046 * <p/> 047 * Utility methods to help preparing {@link RouteDefinition} before they are 048 * added to {@link org.apache.camel.CamelContext}. 049 */ 050@SuppressWarnings({"unchecked", "rawtypes", "deprecation"}) 051public final class RouteDefinitionHelper { 052 053 private RouteDefinitionHelper() { 054 } 055 056 /** 057 * Gather all the endpoint uri's the route is using from the EIPs that has a 058 * static endpoint defined. 059 * 060 * @param route the route 061 * @param includeInputs whether to include inputs 062 * @param includeOutputs whether to include outputs 063 * @return the endpoints uris 064 */ 065 public static Set<String> gatherAllStaticEndpointUris(CamelContext camelContext, RouteDefinition route, boolean includeInputs, boolean includeOutputs) { 066 return gatherAllEndpointUris(camelContext, route, includeInputs, includeOutputs, false); 067 } 068 069 /** 070 * Gather all the endpoint uri's the route is using from the EIPs that has a 071 * static or dynamic endpoint defined. 072 * 073 * @param route the route 074 * @param includeInput whether to include inputs 075 * @param includeOutputs whether to include outputs 076 * @param includeDynamic whether to include dynamic outputs which has been 077 * in use during routing at runtime, gathered from the 078 * {@link org.apache.camel.spi.RuntimeEndpointRegistry}. 079 * @return the endpoints uris 080 */ 081 public static Set<String> gatherAllEndpointUris(CamelContext camelContext, RouteDefinition route, boolean includeInput, boolean includeOutputs, boolean includeDynamic) { 082 Set<String> answer = new LinkedHashSet<>(); 083 084 if (includeInput) { 085 String uri = normalizeUri(route.getInput().getEndpointUri()); 086 if (uri != null) { 087 answer.add(uri); 088 } 089 } 090 091 if (includeOutputs) { 092 Iterator<EndpointRequiredDefinition> it = filterTypeInOutputs(route.getOutputs(), EndpointRequiredDefinition.class); 093 while (it.hasNext()) { 094 String uri = normalizeUri(it.next().getEndpointUri()); 095 if (uri != null) { 096 answer.add(uri); 097 } 098 } 099 if (includeDynamic && camelContext.getRuntimeEndpointRegistry() != null) { 100 List<String> endpoints = camelContext.getRuntimeEndpointRegistry().getEndpointsPerRoute(route.getId(), false); 101 for (String uri : endpoints) { 102 if (uri != null) { 103 answer.add(uri); 104 } 105 } 106 } 107 } 108 109 return answer; 110 } 111 112 private static String normalizeUri(String uri) { 113 try { 114 return URISupport.normalizeUri(uri); 115 } catch (UnsupportedEncodingException e) { 116 // ignore 117 } catch (URISyntaxException e) { 118 // ignore 119 } 120 return null; 121 } 122 123 /** 124 * Force assigning ids to the routes 125 * 126 * @param context the camel context 127 * @param routes the routes 128 * @throws Exception is thrown if error force assign ids to the routes 129 */ 130 public static void forceAssignIds(CamelContext context, List<RouteDefinition> routes) throws Exception { 131 // handle custom assigned id's first, and then afterwards assign auto 132 // generated ids 133 Set<String> customIds = new HashSet<>(); 134 135 for (final RouteDefinition route : routes) { 136 // if there was a custom id assigned, then make sure to support 137 // property placeholders 138 if (route.hasCustomIdAssigned()) { 139 final String originalId = route.getId(); 140 final String id = context.resolvePropertyPlaceholders(originalId); 141 // only set id if its changed, such as we did property 142 // placeholder 143 if (!originalId.equals(id)) { 144 route.setId(id); 145 } 146 customIds.add(id); 147 } else { 148 RestDefinition rest = route.getRestDefinition(); 149 if (rest != null && route.isRest()) { 150 VerbDefinition verb = findVerbDefinition(rest, route.getInput().getEndpointUri()); 151 if (verb != null) { 152 String id = verb.getId(); 153 if (verb.hasCustomIdAssigned() && ObjectHelper.isNotEmpty(id) && !customIds.contains(id)) { 154 route.setId(id); 155 customIds.add(id); 156 } 157 } 158 } 159 } 160 } 161 162 // auto assign route ids 163 for (final RouteDefinition route : routes) { 164 if (route.getId() == null) { 165 // keep assigning id's until we find a free name 166 167 boolean done = false; 168 String id = null; 169 int attempts = 0; 170 while (!done && attempts < 1000) { 171 attempts++; 172 id = route.idOrCreate(context.adapt(ExtendedCamelContext.class).getNodeIdFactory()); 173 if (customIds.contains(id)) { 174 // reset id and try again 175 route.setId(null); 176 } else { 177 done = true; 178 } 179 } 180 if (!done) { 181 throw new IllegalArgumentException("Cannot auto assign id to route: " + route); 182 } 183 route.setId(id); 184 route.setCustomId(false); 185 customIds.add(route.getId()); 186 } 187 RestDefinition rest = route.getRestDefinition(); 188 if (rest != null && route.isRest()) { 189 VerbDefinition verb = findVerbDefinition(rest, route.getInput().getEndpointUri()); 190 if (verb != null) { 191 String id = verb.idOrCreate(context.adapt(ExtendedCamelContext.class).getNodeIdFactory()); 192 if (!verb.getUsedForGeneratingNodeId()) { 193 id = route.getId(); 194 } 195 verb.setRouteId(id); 196 } 197 198 // if its the rest/rest-api endpoints then they should include 199 // the route id as well 200 if (ObjectHelper.isNotEmpty(route.getInput())) { 201 FromDefinition fromDefinition = route.getInput(); 202 String endpointUri = fromDefinition.getEndpointUri(); 203 if (ObjectHelper.isNotEmpty(endpointUri) && (endpointUri.startsWith("rest:") || endpointUri.startsWith("rest-api:"))) { 204 Map<String, Object> options = new HashMap<>(1); 205 options.put("routeId", route.getId()); 206 endpointUri = URISupport.appendParametersToURI(endpointUri, options); 207 208 // replace uri with new routeId 209 fromDefinition.setUri(endpointUri); 210 route.setInput(fromDefinition); 211 } 212 } 213 } 214 } 215 } 216 217 /** 218 * Find verb associated with the route by mapping uri 219 */ 220 private static VerbDefinition findVerbDefinition(RestDefinition rest, String endpointUri) { 221 VerbDefinition ret = null; 222 String preVerbUri = ""; 223 for (VerbDefinition verb : rest.getVerbs()) { 224 String verbUri = rest.buildFromUri(verb); 225 if (endpointUri.startsWith(verbUri) && preVerbUri.length() < verbUri.length()) { 226 // if there are multiple verb uri match, select the most 227 // specific one 228 // for example if the endpoint Uri is 229 // rest:get:/user:/{id}/user?produces=text%2Fplain 230 // then the verbUri rest:get:/user:/{id}/user should overweigh 231 // the est:get:/user:/{id} 232 preVerbUri = verbUri; 233 ret = verb; 234 } 235 } 236 return ret; 237 } 238 239 /** 240 * Validates that the target route has no duplicate id's from any of the 241 * existing routes. 242 * 243 * @param target the target route 244 * @param routes the existing routes 245 * @return <tt>null</tt> if no duplicate id's detected, otherwise the first 246 * found duplicate id is returned. 247 */ 248 public static String validateUniqueIds(RouteDefinition target, List<RouteDefinition> routes) { 249 Set<String> routesIds = new LinkedHashSet<>(); 250 // gather all ids for the existing route, but only include custom ids, 251 // and no abstract ids 252 // as abstract nodes is cross-cutting functionality such as interceptors 253 // etc 254 for (RouteDefinition route : routes) { 255 // skip target route as we gather ids in a separate set 256 if (route == target) { 257 continue; 258 } 259 ProcessorDefinitionHelper.gatherAllNodeIds(route, routesIds, true, false); 260 } 261 262 // gather all ids for the target route, but only include custom ids, and 263 // no abstract ids 264 // as abstract nodes is cross-cutting functionality such as interceptors 265 // etc 266 Set<String> targetIds = new LinkedHashSet<>(); 267 ProcessorDefinitionHelper.gatherAllNodeIds(target, targetIds, true, false); 268 269 // now check for clash with the target route 270 for (String id : targetIds) { 271 if (routesIds.contains(id)) { 272 return id; 273 } 274 } 275 276 return null; 277 } 278 279 public static void initParent(ProcessorDefinition parent) { 280 List<ProcessorDefinition<?>> children = parent.getOutputs(); 281 for (ProcessorDefinition child : children) { 282 child.setParent(parent); 283 if (child.getOutputs() != null && !child.getOutputs().isEmpty()) { 284 // recursive the children 285 initParent(child); 286 } 287 } 288 } 289 290 public static void prepareRouteForInit(RouteDefinition route, List<ProcessorDefinition<?>> abstracts, List<ProcessorDefinition<?>> lower) { 291 // filter the route into abstracts and lower 292 for (ProcessorDefinition output : route.getOutputs()) { 293 if (output.isAbstract()) { 294 abstracts.add(output); 295 } else { 296 lower.add(output); 297 } 298 } 299 } 300 301 /** 302 * Prepares the route. 303 * <p/> 304 * This method does <b>not</b> mark the route as prepared afterwards. 305 * 306 * @param context the camel context 307 * @param route the route 308 */ 309 public static void prepareRoute(CamelContext context, RouteDefinition route) { 310 prepareRoute(context, route, null, null, null, null, null); 311 } 312 313 /** 314 * Prepares the route which supports context scoped features such as 315 * onException, interceptors and onCompletions 316 * <p/> 317 * This method does <b>not</b> mark the route as prepared afterwards. 318 * 319 * @param context the camel context 320 * @param route the route 321 * @param onExceptions optional list of onExceptions 322 * @param intercepts optional list of interceptors 323 * @param interceptFromDefinitions optional list of interceptFroms 324 * @param interceptSendToEndpointDefinitions optional list of 325 * interceptSendToEndpoints 326 * @param onCompletions optional list onCompletions 327 */ 328 public static void prepareRoute(CamelContext context, RouteDefinition route, List<OnExceptionDefinition> onExceptions, List<InterceptDefinition> intercepts, 329 List<InterceptFromDefinition> interceptFromDefinitions, List<InterceptSendToEndpointDefinition> interceptSendToEndpointDefinitions, 330 List<OnCompletionDefinition> onCompletions) { 331 332 prepareRouteImp(context, route, onExceptions, intercepts, interceptFromDefinitions, interceptSendToEndpointDefinitions, onCompletions); 333 } 334 335 /** 336 * Prepares the route which supports context scoped features such as 337 * onException, interceptors and onCompletions 338 * <p/> 339 * This method does <b>not</b> mark the route as prepared afterwards. 340 * 341 * @param context the camel context 342 * @param route the route 343 * @param onExceptions optional list of onExceptions 344 * @param intercepts optional list of interceptors 345 * @param interceptFromDefinitions optional list of interceptFroms 346 * @param interceptSendToEndpointDefinitions optional list of 347 * interceptSendToEndpoints 348 * @param onCompletions optional list onCompletions 349 */ 350 private static void prepareRouteImp(CamelContext context, RouteDefinition route, List<OnExceptionDefinition> onExceptions, List<InterceptDefinition> intercepts, 351 List<InterceptFromDefinition> interceptFromDefinitions, List<InterceptSendToEndpointDefinition> interceptSendToEndpointDefinitions, 352 List<OnCompletionDefinition> onCompletions) { 353 354 // init the route inputs 355 initRouteInput(context, route.getInput()); 356 357 // abstracts is the cross cutting concerns 358 List<ProcessorDefinition<?>> abstracts = new ArrayList<>(); 359 360 // upper is the cross cutting concerns such as interceptors, error 361 // handlers etc 362 List<ProcessorDefinition<?>> upper = new ArrayList<>(); 363 364 // lower is the regular route 365 List<ProcessorDefinition<?>> lower = new ArrayList<>(); 366 367 RouteDefinitionHelper.prepareRouteForInit(route, abstracts, lower); 368 369 // parent and error handler builder should be initialized first 370 initParentAndErrorHandlerBuilder(context, route, abstracts, onExceptions); 371 // validate top-level violations 372 validateTopLevel(route.getOutputs()); 373 // then interceptors 374 initInterceptors(context, route, abstracts, upper, intercepts, interceptFromDefinitions, interceptSendToEndpointDefinitions); 375 // then on completion 376 initOnCompletions(abstracts, upper, onCompletions); 377 // then sagas 378 initSagas(abstracts, lower); 379 // then transactions 380 initTransacted(abstracts, lower); 381 // then on exception 382 initOnExceptions(abstracts, upper, onExceptions); 383 384 // rebuild route as upper + lower 385 route.clearOutput(); 386 route.getOutputs().addAll(lower); 387 route.getOutputs().addAll(0, upper); 388 } 389 390 /** 391 * Sanity check the route, that it has input(s) and outputs. 392 * 393 * @param route the route 394 * @throws IllegalArgumentException is thrown if the route is invalid 395 */ 396 public static void sanityCheckRoute(RouteDefinition route) { 397 ObjectHelper.notNull(route, "route"); 398 399 if (route.getInput() == null) { 400 String msg = "Route has no inputs: " + route; 401 if (route.getId() != null) { 402 msg = "Route " + route.getId() + " has no inputs: " + route; 403 } 404 throw new IllegalArgumentException(msg); 405 } 406 407 if (route.getOutputs() == null || route.getOutputs().isEmpty()) { 408 String msg = "Route has no outputs: " + route; 409 if (route.getId() != null) { 410 msg = "Route " + route.getId() + " has no outputs: " + route; 411 } 412 throw new IllegalArgumentException(msg); 413 } 414 } 415 416 /** 417 * Validates that top-level only definitions is not added in the wrong 418 * places, such as nested inside a splitter etc. 419 */ 420 private static void validateTopLevel(List<ProcessorDefinition<?>> children) { 421 for (ProcessorDefinition child : children) { 422 // validate that top-level is only added on the route (eg top level) 423 RouteDefinition route = ProcessorDefinitionHelper.getRoute(child); 424 boolean parentIsRoute = child.getParent() == route; 425 if (child.isTopLevelOnly() && !parentIsRoute) { 426 throw new IllegalArgumentException("The output must be added as top-level on the route. Try moving " + child + " to the top of route."); 427 } 428 if (child.getOutputs() != null && !child.getOutputs().isEmpty()) { 429 validateTopLevel(child.getOutputs()); 430 } 431 } 432 } 433 434 private static void initRouteInput(CamelContext camelContext, FromDefinition input) { 435 // noop 436 } 437 438 private static void initParentAndErrorHandlerBuilder(CamelContext context, RouteDefinition route, List<ProcessorDefinition<?>> abstracts, 439 List<OnExceptionDefinition> onExceptions) { 440 441 if (context != null) { 442 // let the route inherit the error handler builder from camel 443 // context if none already set 444 445 // must clone to avoid side effects while building routes using 446 // multiple RouteBuilders 447 ErrorHandlerFactory builder = context.adapt(ExtendedCamelContext.class).getErrorHandlerFactory(); 448 if (builder != null) { 449 if (builder instanceof ErrorHandlerBuilder) { 450 builder = ((ErrorHandlerBuilder)builder).cloneBuilder(); 451 route.setErrorHandlerFactoryIfNull(builder); 452 } else { 453 throw new UnsupportedOperationException("The ErrorHandlerFactory must implement ErrorHandlerBuilder"); 454 } 455 } 456 } 457 458 // init parent and error handler builder on the route 459 initParent(route); 460 461 // set the parent and error handler builder on the global on exceptions 462 if (onExceptions != null) { 463 for (OnExceptionDefinition global : onExceptions) { 464 initParent(global); 465 } 466 } 467 } 468 469 private static void initOnExceptions(List<ProcessorDefinition<?>> abstracts, List<ProcessorDefinition<?>> upper, List<OnExceptionDefinition> onExceptions) { 470 // add global on exceptions if any 471 if (onExceptions != null && !onExceptions.isEmpty()) { 472 for (OnExceptionDefinition output : onExceptions) { 473 // these are context scoped on exceptions so set this flag 474 output.setRouteScoped(false); 475 abstracts.add(output); 476 } 477 } 478 479 // now add onExceptions to the route 480 for (ProcessorDefinition output : abstracts) { 481 if (output instanceof OnExceptionDefinition) { 482 // on exceptions must be added at top, so the route flow is 483 // correct as 484 // on exceptions should be the first outputs 485 486 // find the index to add the on exception, it should be in the 487 // top 488 // but it should add itself after any existing onException 489 int index = 0; 490 for (int i = 0; i < upper.size(); i++) { 491 ProcessorDefinition up = upper.get(i); 492 if (!(up instanceof OnExceptionDefinition)) { 493 index = i; 494 break; 495 } else { 496 index++; 497 } 498 } 499 upper.add(index, output); 500 } 501 } 502 } 503 504 private static void initInterceptors(CamelContext context, RouteDefinition route, List<ProcessorDefinition<?>> abstracts, List<ProcessorDefinition<?>> upper, 505 List<InterceptDefinition> intercepts, List<InterceptFromDefinition> interceptFromDefinitions, 506 List<InterceptSendToEndpointDefinition> interceptSendToEndpointDefinitions) { 507 508 // move the abstracts interceptors into the dedicated list 509 for (ProcessorDefinition processor : abstracts) { 510 if (processor instanceof InterceptSendToEndpointDefinition) { 511 if (interceptSendToEndpointDefinitions == null) { 512 interceptSendToEndpointDefinitions = new ArrayList<>(); 513 } 514 interceptSendToEndpointDefinitions.add((InterceptSendToEndpointDefinition)processor); 515 } else if (processor instanceof InterceptFromDefinition) { 516 if (interceptFromDefinitions == null) { 517 interceptFromDefinitions = new ArrayList<>(); 518 } 519 interceptFromDefinitions.add((InterceptFromDefinition)processor); 520 } else if (processor instanceof InterceptDefinition) { 521 if (intercepts == null) { 522 intercepts = new ArrayList<>(); 523 } 524 intercepts.add((InterceptDefinition)processor); 525 } 526 } 527 528 doInitInterceptors(context, route, upper, intercepts, interceptFromDefinitions, interceptSendToEndpointDefinitions); 529 } 530 531 private static void doInitInterceptors(CamelContext context, RouteDefinition route, List<ProcessorDefinition<?>> upper, List<InterceptDefinition> intercepts, 532 List<InterceptFromDefinition> interceptFromDefinitions, List<InterceptSendToEndpointDefinition> interceptSendToEndpointDefinitions) { 533 534 // configure intercept 535 if (intercepts != null && !intercepts.isEmpty()) { 536 for (InterceptDefinition intercept : intercepts) { 537 intercept.afterPropertiesSet(); 538 // init the parent 539 initParent(intercept); 540 // add as first output so intercept is handled before the actual 541 // route and that gives 542 // us the needed head start to init and be able to intercept all 543 // the remaining processing steps 544 upper.add(0, intercept); 545 } 546 } 547 548 // configure intercept from 549 if (interceptFromDefinitions != null && !interceptFromDefinitions.isEmpty()) { 550 for (InterceptFromDefinition intercept : interceptFromDefinitions) { 551 552 // should we only apply interceptor for a given endpoint uri 553 boolean match = true; 554 if (intercept.getUri() != null) { 555 556 // the uri can have property placeholders so resolve them 557 // first 558 String pattern; 559 try { 560 pattern = context.resolvePropertyPlaceholders(intercept.getUri()); 561 } catch (Exception e) { 562 throw RuntimeCamelException.wrapRuntimeCamelException(e); 563 } 564 boolean isRefPattern = pattern.startsWith("ref*") || pattern.startsWith("ref:"); 565 566 match = false; 567 568 // a bit more logic to lookup the endpoint as it can be 569 // uri/ref based 570 String uri = route.getInput().getEndpointUri(); 571 // if the pattern is not a ref itself, then resolve the ref 572 // uris, so we can match the actual uri's with each other 573 if (!isRefPattern) { 574 if (uri != null && uri.startsWith("ref:")) { 575 // its a ref: so lookup the endpoint to get its url 576 String ref = uri.substring(4); 577 uri = CamelContextHelper.getMandatoryEndpoint(context, ref).getEndpointUri(); 578 } 579 } 580 if (EndpointHelper.matchEndpoint(context, uri, pattern)) { 581 match = true; 582 } 583 } 584 585 if (match) { 586 intercept.afterPropertiesSet(); 587 // init the parent 588 initParent(intercept); 589 // add as first output so intercept is handled before the 590 // actual route and that gives 591 // us the needed head start to init and be able to intercept 592 // all the remaining processing steps 593 upper.add(0, intercept); 594 } 595 } 596 } 597 598 // configure intercept send to endpoint 599 if (interceptSendToEndpointDefinitions != null && !interceptSendToEndpointDefinitions.isEmpty()) { 600 for (InterceptSendToEndpointDefinition intercept : interceptSendToEndpointDefinitions) { 601 intercept.afterPropertiesSet(); 602 // init the parent 603 initParent(intercept); 604 // add as first output so intercept is handled before the actual 605 // route and that gives 606 // us the needed head start to init and be able to intercept all 607 // the remaining processing steps 608 upper.add(0, intercept); 609 } 610 } 611 } 612 613 private static void initOnCompletions(List<ProcessorDefinition<?>> abstracts, List<ProcessorDefinition<?>> upper, List<OnCompletionDefinition> onCompletions) { 614 List<OnCompletionDefinition> completions = new ArrayList<>(); 615 616 // find the route scoped onCompletions 617 for (ProcessorDefinition out : abstracts) { 618 if (out instanceof OnCompletionDefinition) { 619 completions.add((OnCompletionDefinition)out); 620 } 621 } 622 623 // only add global onCompletion if there are no route already 624 if (completions.isEmpty() && onCompletions != null) { 625 completions = onCompletions; 626 // init the parent 627 for (OnCompletionDefinition global : completions) { 628 initParent(global); 629 } 630 } 631 632 // are there any completions to init at all? 633 if (completions.isEmpty()) { 634 return; 635 } 636 637 upper.addAll(completions); 638 } 639 640 private static void initSagas(List<ProcessorDefinition<?>> abstracts, List<ProcessorDefinition<?>> lower) { 641 SagaDefinition saga = null; 642 643 // add to correct type 644 for (ProcessorDefinition<?> type : abstracts) { 645 if (type instanceof SagaDefinition) { 646 if (saga == null) { 647 saga = (SagaDefinition)type; 648 } else { 649 throw new IllegalArgumentException("The route can only have one saga defined"); 650 } 651 } 652 } 653 654 if (saga != null) { 655 // the outputs should be moved to the transacted policy 656 saga.getOutputs().addAll(lower); 657 // and add it as the single output 658 lower.clear(); 659 lower.add(saga); 660 } 661 } 662 663 private static void initTransacted(List<ProcessorDefinition<?>> abstracts, List<ProcessorDefinition<?>> lower) { 664 TransactedDefinition transacted = null; 665 666 // add to correct type 667 for (ProcessorDefinition<?> type : abstracts) { 668 if (type instanceof TransactedDefinition) { 669 if (transacted == null) { 670 transacted = (TransactedDefinition)type; 671 } else { 672 throw new IllegalArgumentException("The route can only have one transacted defined"); 673 } 674 } 675 } 676 677 if (transacted != null) { 678 // the outputs should be moved to the transacted policy 679 transacted.getOutputs().addAll(lower); 680 // and add it as the single output 681 lower.clear(); 682 lower.add(transacted); 683 } 684 } 685 686 /** 687 * Force assigning ids to the give node and all its children (recursively). 688 * <p/> 689 * This is needed when doing tracing or the likes, where each node should 690 * have its id assigned so the tracing can pin point exactly. 691 * 692 * @param context the camel context 693 * @param processor the node 694 */ 695 public static void forceAssignIds(CamelContext context, final ProcessorDefinition processor) { 696 // force id on the child 697 processor.idOrCreate(context.adapt(ExtendedCamelContext.class).getNodeIdFactory()); 698 699 // if there was a custom id assigned, then make sure to support property 700 // placeholders 701 if (processor.hasCustomIdAssigned()) { 702 try { 703 final String originalId = processor.getId(); 704 String id = context.resolvePropertyPlaceholders(originalId); 705 // only set id if its changed, such as we did property 706 // placeholder 707 if (!originalId.equals(id)) { 708 processor.setId(id); 709 } 710 } catch (Exception e) { 711 throw RuntimeCamelException.wrapRuntimeCamelException(e); 712 } 713 } 714 715 List<ProcessorDefinition<?>> children = processor.getOutputs(); 716 if (children != null && !children.isEmpty()) { 717 for (ProcessorDefinition child : children) { 718 forceAssignIds(context, child); 719 } 720 } 721 } 722 723 public static String getRouteMessage(String route) { 724 // ensure to sanitize uri's in the route so we do not show sensitive 725 // information such as passwords 726 route = URISupport.sanitizeUri(route); 727 // cut the route after 60 chars so it won't be too big in the message 728 // users just need to be able to identify the route so they know where 729 // to look 730 if (route.length() > 60) { 731 return route.substring(0, 60) + "..."; 732 } else { 733 return route; 734 } 735 } 736}