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