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