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.reifier; 018 019import java.util.ArrayList; 020import java.util.Collection; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.concurrent.ExecutorService; 025import java.util.concurrent.ScheduledExecutorService; 026import java.util.function.BiFunction; 027 028import org.apache.camel.CamelContext; 029import org.apache.camel.Channel; 030import org.apache.camel.ErrorHandlerFactory; 031import org.apache.camel.ExtendedCamelContext; 032import org.apache.camel.Processor; 033import org.apache.camel.model.AggregateDefinition; 034import org.apache.camel.model.BeanDefinition; 035import org.apache.camel.model.CatchDefinition; 036import org.apache.camel.model.ChoiceDefinition; 037import org.apache.camel.model.CircuitBreakerDefinition; 038import org.apache.camel.model.ClaimCheckDefinition; 039import org.apache.camel.model.ConvertBodyDefinition; 040import org.apache.camel.model.DelayDefinition; 041import org.apache.camel.model.DynamicRouterDefinition; 042import org.apache.camel.model.EnrichDefinition; 043import org.apache.camel.model.ExecutorServiceAwareDefinition; 044import org.apache.camel.model.FilterDefinition; 045import org.apache.camel.model.FinallyDefinition; 046import org.apache.camel.model.IdempotentConsumerDefinition; 047import org.apache.camel.model.InOnlyDefinition; 048import org.apache.camel.model.InOutDefinition; 049import org.apache.camel.model.InterceptDefinition; 050import org.apache.camel.model.InterceptFromDefinition; 051import org.apache.camel.model.InterceptSendToEndpointDefinition; 052import org.apache.camel.model.LoadBalanceDefinition; 053import org.apache.camel.model.LogDefinition; 054import org.apache.camel.model.LoopDefinition; 055import org.apache.camel.model.MarshalDefinition; 056import org.apache.camel.model.MulticastDefinition; 057import org.apache.camel.model.OnCompletionDefinition; 058import org.apache.camel.model.OnExceptionDefinition; 059import org.apache.camel.model.OnFallbackDefinition; 060import org.apache.camel.model.OptionalIdentifiedDefinition; 061import org.apache.camel.model.OtherwiseDefinition; 062import org.apache.camel.model.PipelineDefinition; 063import org.apache.camel.model.PolicyDefinition; 064import org.apache.camel.model.PollEnrichDefinition; 065import org.apache.camel.model.ProcessDefinition; 066import org.apache.camel.model.ProcessorDefinition; 067import org.apache.camel.model.ProcessorDefinitionHelper; 068import org.apache.camel.model.RecipientListDefinition; 069import org.apache.camel.model.RemoveHeaderDefinition; 070import org.apache.camel.model.RemoveHeadersDefinition; 071import org.apache.camel.model.RemovePropertiesDefinition; 072import org.apache.camel.model.RemovePropertyDefinition; 073import org.apache.camel.model.ResequenceDefinition; 074import org.apache.camel.model.RollbackDefinition; 075import org.apache.camel.model.RouteDefinition; 076import org.apache.camel.model.RouteDefinitionHelper; 077import org.apache.camel.model.RoutingSlipDefinition; 078import org.apache.camel.model.SagaDefinition; 079import org.apache.camel.model.SamplingDefinition; 080import org.apache.camel.model.ScriptDefinition; 081import org.apache.camel.model.SetBodyDefinition; 082import org.apache.camel.model.SetExchangePatternDefinition; 083import org.apache.camel.model.SetHeaderDefinition; 084import org.apache.camel.model.SetPropertyDefinition; 085import org.apache.camel.model.SortDefinition; 086import org.apache.camel.model.SplitDefinition; 087import org.apache.camel.model.StepDefinition; 088import org.apache.camel.model.StopDefinition; 089import org.apache.camel.model.ThreadsDefinition; 090import org.apache.camel.model.ThrottleDefinition; 091import org.apache.camel.model.ThrowExceptionDefinition; 092import org.apache.camel.model.ToDefinition; 093import org.apache.camel.model.ToDynamicDefinition; 094import org.apache.camel.model.TransactedDefinition; 095import org.apache.camel.model.TransformDefinition; 096import org.apache.camel.model.TryDefinition; 097import org.apache.camel.model.UnmarshalDefinition; 098import org.apache.camel.model.ValidateDefinition; 099import org.apache.camel.model.WhenDefinition; 100import org.apache.camel.model.WhenSkipSendToEndpointDefinition; 101import org.apache.camel.model.WireTapDefinition; 102import org.apache.camel.model.cloud.ServiceCallDefinition; 103import org.apache.camel.processor.InterceptEndpointProcessor; 104import org.apache.camel.processor.Pipeline; 105import org.apache.camel.processor.channel.DefaultChannel; 106import org.apache.camel.reifier.errorhandler.ErrorHandlerReifier; 107import org.apache.camel.spi.ExecutorServiceManager; 108import org.apache.camel.spi.IdAware; 109import org.apache.camel.spi.InterceptStrategy; 110import org.apache.camel.spi.LifecycleStrategy; 111import org.apache.camel.spi.ReifierStrategy; 112import org.apache.camel.spi.RouteContext; 113import org.apache.camel.spi.RouteIdAware; 114import org.apache.camel.support.CamelContextHelper; 115import org.apache.camel.util.ObjectHelper; 116import org.slf4j.Logger; 117import org.slf4j.LoggerFactory; 118 119public abstract class ProcessorReifier<T extends ProcessorDefinition<?>> extends AbstractReifier { 120 121 private static final Map<Class<?>, BiFunction<RouteContext, ProcessorDefinition<?>, ProcessorReifier<? extends ProcessorDefinition<?>>>> PROCESSORS; 122 static { 123 // NOTE: if adding a new class then update the initial capacity of the 124 // HashMap 125 Map<Class<?>, BiFunction<RouteContext, ProcessorDefinition<?>, ProcessorReifier<? extends ProcessorDefinition<?>>>> map = new HashMap<>(65); 126 map.put(AggregateDefinition.class, AggregateReifier::new); 127 map.put(BeanDefinition.class, BeanReifier::new); 128 map.put(CatchDefinition.class, CatchReifier::new); 129 map.put(ChoiceDefinition.class, ChoiceReifier::new); 130 map.put(CircuitBreakerDefinition.class, CircuitBreakerReifier::new); 131 map.put(ClaimCheckDefinition.class, ClaimCheckReifier::new); 132 map.put(ConvertBodyDefinition.class, ConvertBodyReifier::new); 133 map.put(DelayDefinition.class, DelayReifier::new); 134 map.put(DynamicRouterDefinition.class, DynamicRouterReifier::new); 135 map.put(EnrichDefinition.class, EnrichReifier::new); 136 map.put(FilterDefinition.class, FilterReifier::new); 137 map.put(FinallyDefinition.class, FinallyReifier::new); 138 map.put(IdempotentConsumerDefinition.class, IdempotentConsumerReifier::new); 139 map.put(InOnlyDefinition.class, SendReifier::new); 140 map.put(InOutDefinition.class, SendReifier::new); 141 map.put(InterceptDefinition.class, InterceptReifier::new); 142 map.put(InterceptFromDefinition.class, InterceptFromReifier::new); 143 map.put(InterceptSendToEndpointDefinition.class, InterceptSendToEndpointReifier::new); 144 map.put(LoadBalanceDefinition.class, LoadBalanceReifier::new); 145 map.put(LogDefinition.class, LogReifier::new); 146 map.put(LoopDefinition.class, LoopReifier::new); 147 map.put(MarshalDefinition.class, MarshalReifier::new); 148 map.put(MulticastDefinition.class, MulticastReifier::new); 149 map.put(OnCompletionDefinition.class, OnCompletionReifier::new); 150 map.put(OnExceptionDefinition.class, OnExceptionReifier::new); 151 map.put(OnFallbackDefinition.class, OnFallbackReifier::new); 152 map.put(OtherwiseDefinition.class, OtherwiseReifier::new); 153 map.put(PipelineDefinition.class, PipelineReifier::new); 154 map.put(PolicyDefinition.class, PolicyReifier::new); 155 map.put(PollEnrichDefinition.class, PollEnrichReifier::new); 156 map.put(ProcessDefinition.class, ProcessReifier::new); 157 map.put(RecipientListDefinition.class, RecipientListReifier::new); 158 map.put(RemoveHeaderDefinition.class, RemoveHeaderReifier::new); 159 map.put(RemoveHeadersDefinition.class, RemoveHeadersReifier::new); 160 map.put(RemovePropertiesDefinition.class, RemovePropertiesReifier::new); 161 map.put(RemovePropertyDefinition.class, RemovePropertyReifier::new); 162 map.put(ResequenceDefinition.class, ResequenceReifier::new); 163 map.put(RollbackDefinition.class, RollbackReifier::new); 164 map.put(RouteDefinition.class, RouteReifier::new); 165 map.put(RoutingSlipDefinition.class, RoutingSlipReifier::new); 166 map.put(SagaDefinition.class, SagaReifier::new); 167 map.put(SamplingDefinition.class, SamplingReifier::new); 168 map.put(ScriptDefinition.class, ScriptReifier::new); 169 map.put(ServiceCallDefinition.class, ServiceCallReifier::new); 170 map.put(SetBodyDefinition.class, SetBodyReifier::new); 171 map.put(SetExchangePatternDefinition.class, SetExchangePatternReifier::new); 172 map.put(SetHeaderDefinition.class, SetHeaderReifier::new); 173 map.put(SetPropertyDefinition.class, SetPropertyReifier::new); 174 map.put(SortDefinition.class, SortReifier::new); 175 map.put(SplitDefinition.class, SplitReifier::new); 176 map.put(StepDefinition.class, StepReifier::new); 177 map.put(StopDefinition.class, StopReifier::new); 178 map.put(ThreadsDefinition.class, ThreadsReifier::new); 179 map.put(ThrottleDefinition.class, ThrottleReifier::new); 180 map.put(ThrowExceptionDefinition.class, ThrowExceptionReifier::new); 181 map.put(ToDefinition.class, SendReifier::new); 182 map.put(ToDynamicDefinition.class, ToDynamicReifier::new); 183 map.put(TransactedDefinition.class, TransactedReifier::new); 184 map.put(TransformDefinition.class, TransformReifier::new); 185 map.put(TryDefinition.class, TryReifier::new); 186 map.put(UnmarshalDefinition.class, UnmarshalReifier::new); 187 map.put(ValidateDefinition.class, ValidateReifier::new); 188 map.put(WireTapDefinition.class, WireTapReifier::new); 189 map.put(WhenSkipSendToEndpointDefinition.class, WhenSkipSendToEndpointReifier::new); 190 map.put(WhenDefinition.class, WhenReifier::new); 191 PROCESSORS = map; 192 ReifierStrategy.addReifierClearer(ProcessorReifier::clearReifiers); 193 } 194 protected final Logger log = LoggerFactory.getLogger(getClass()); 195 196 protected final T definition; 197 198 public ProcessorReifier(RouteContext routeContext, T definition) { 199 super(routeContext); 200 this.definition = definition; 201 } 202 203 public ProcessorReifier(CamelContext camelContext, T definition) { 204 super(camelContext); 205 this.definition = definition; 206 } 207 208 public static void registerReifier(Class<?> processorClass, BiFunction<RouteContext, ProcessorDefinition<?>, ProcessorReifier<? extends ProcessorDefinition<?>>> creator) { 209 PROCESSORS.put(processorClass, creator); 210 } 211 212 public static void clearReifiers() { 213 PROCESSORS.clear(); 214 } 215 216 public static ProcessorReifier<? extends ProcessorDefinition<?>> reifier(RouteContext routeContext, ProcessorDefinition<?> definition) { 217 BiFunction<RouteContext, ProcessorDefinition<?>, ProcessorReifier<? extends ProcessorDefinition<?>>> reifier = PROCESSORS.get(definition.getClass()); 218 if (reifier != null) { 219 return reifier.apply(routeContext, definition); 220 } 221 throw new IllegalStateException("Unsupported definition: " + definition); 222 } 223 224 /** 225 * Determines whether a new thread pool will be created or not. 226 * <p/> 227 * This is used to know if a new thread pool will be created, and therefore 228 * is not shared by others, and therefore exclusive to the definition. 229 * 230 * @param definition the node definition which may leverage executor 231 * service. 232 * @param useDefault whether to fallback and use a default thread pool, if 233 * no explicit configured 234 * @return <tt>true</tt> if a new thread pool will be created, 235 * <tt>false</tt> if not 236 * @see #getConfiguredExecutorService(String, ExecutorServiceAwareDefinition, boolean) 237 */ 238 public boolean willCreateNewThreadPool(ExecutorServiceAwareDefinition<?> definition, boolean useDefault) { 239 ExecutorServiceManager manager = camelContext.getExecutorServiceManager(); 240 ObjectHelper.notNull(manager, "ExecutorServiceManager", camelContext); 241 242 if (definition.getExecutorService() != null) { 243 // no there is a custom thread pool configured 244 return false; 245 } else if (definition.getExecutorServiceRef() != null) { 246 ExecutorService answer = routeContext.lookup(definition.getExecutorServiceRef(), ExecutorService.class); 247 // if no existing thread pool, then we will have to create a new 248 // thread pool 249 return answer == null; 250 } else if (useDefault) { 251 return true; 252 } 253 254 return false; 255 } 256 257 /** 258 * Will lookup and get the configured 259 * {@link ExecutorService} from the given definition. 260 * <p/> 261 * This method will lookup for configured thread pool in the following order 262 * <ul> 263 * <li>from the definition if any explicit configured executor service.</li> 264 * <li>from the {@link org.apache.camel.spi.Registry} if found</li> 265 * <li>from the known list of {@link org.apache.camel.spi.ThreadPoolProfile 266 * ThreadPoolProfile(s)}.</li> 267 * <li>if none found, then <tt>null</tt> is returned.</li> 268 * </ul> 269 * The various {@link ExecutorServiceAwareDefinition} should use this helper 270 * method to ensure they support configured executor services in the same 271 * coherent way. 272 * 273 * @param name name which is appended to the thread name, when the 274 * {@link ExecutorService} is created based 275 * on a {@link org.apache.camel.spi.ThreadPoolProfile}. 276 * @param definition the node definition which may leverage executor 277 * service. 278 * @param useDefault whether to fallback and use a default thread pool, if 279 * no explicit configured 280 * @return the configured executor service, or <tt>null</tt> if none was 281 * configured. 282 * @throws IllegalArgumentException is thrown if lookup of executor service 283 * in {@link org.apache.camel.spi.Registry} was not found 284 */ 285 public ExecutorService getConfiguredExecutorService(String name, ExecutorServiceAwareDefinition<?> definition, boolean useDefault) 286 throws IllegalArgumentException { 287 ExecutorServiceManager manager = camelContext.getExecutorServiceManager(); 288 ObjectHelper.notNull(manager, "ExecutorServiceManager", camelContext); 289 290 // prefer to use explicit configured executor on the definition 291 if (definition.getExecutorService() != null) { 292 return definition.getExecutorService(); 293 } else if (definition.getExecutorServiceRef() != null) { 294 // lookup in registry first and use existing thread pool if exists 295 ExecutorService answer = lookupExecutorServiceRef(name, definition, parseString(definition.getExecutorServiceRef())); 296 if (answer == null) { 297 throw new IllegalArgumentException("ExecutorServiceRef " + definition.getExecutorServiceRef() 298 + " not found in registry (as an ExecutorService instance) or as a thread pool profile."); 299 } 300 return answer; 301 } else if (useDefault) { 302 return manager.newDefaultThreadPool(definition, name); 303 } 304 305 return null; 306 } 307 308 /** 309 * Will lookup and get the configured 310 * {@link java.util.concurrent.ScheduledExecutorService} from the given 311 * definition. 312 * <p/> 313 * This method will lookup for configured thread pool in the following order 314 * <ul> 315 * <li>from the definition if any explicit configured executor service.</li> 316 * <li>from the {@link org.apache.camel.spi.Registry} if found</li> 317 * <li>from the known list of {@link org.apache.camel.spi.ThreadPoolProfile 318 * ThreadPoolProfile(s)}.</li> 319 * <li>if none found, then <tt>null</tt> is returned.</li> 320 * </ul> 321 * The various {@link ExecutorServiceAwareDefinition} should use this helper 322 * method to ensure they support configured executor services in the same 323 * coherent way. 324 * 325 * @param name name which is appended to the thread name, when the 326 * {@link ExecutorService} is created based 327 * on a {@link org.apache.camel.spi.ThreadPoolProfile}. 328 * @param definition the node definition which may leverage executor 329 * service. 330 * @param useDefault whether to fallback and use a default thread pool, if 331 * no explicit configured 332 * @return the configured executor service, or <tt>null</tt> if none was 333 * configured. 334 * @throws IllegalArgumentException is thrown if the found instance is not a 335 * ScheduledExecutorService type, or lookup of executor service 336 * in {@link org.apache.camel.spi.Registry} was not found 337 */ 338 public ScheduledExecutorService getConfiguredScheduledExecutorService(String name, ExecutorServiceAwareDefinition<?> definition, 339 boolean useDefault) 340 throws IllegalArgumentException { 341 ExecutorServiceManager manager = camelContext.getExecutorServiceManager(); 342 ObjectHelper.notNull(manager, "ExecutorServiceManager", camelContext); 343 344 // prefer to use explicit configured executor on the definition 345 if (definition.getExecutorService() != null) { 346 ExecutorService executorService = definition.getExecutorService(); 347 if (executorService instanceof ScheduledExecutorService) { 348 return (ScheduledExecutorService)executorService; 349 } 350 throw new IllegalArgumentException("ExecutorServiceRef " + definition.getExecutorServiceRef() + " is not an ScheduledExecutorService instance"); 351 } else if (definition.getExecutorServiceRef() != null) { 352 ScheduledExecutorService answer = lookupScheduledExecutorServiceRef(name, definition, definition.getExecutorServiceRef()); 353 if (answer == null) { 354 throw new IllegalArgumentException("ExecutorServiceRef " + definition.getExecutorServiceRef() 355 + " not found in registry (as an ScheduledExecutorService instance) or as a thread pool profile."); 356 } 357 return answer; 358 } else if (useDefault) { 359 return manager.newDefaultScheduledThreadPool(definition, name); 360 } 361 362 return null; 363 } 364 365 /** 366 * Will lookup in {@link org.apache.camel.spi.Registry} for a 367 * {@link ScheduledExecutorService} registered with the given 368 * <tt>executorServiceRef</tt> name. 369 * <p/> 370 * This method will lookup for configured thread pool in the following order 371 * <ul> 372 * <li>from the {@link org.apache.camel.spi.Registry} if found</li> 373 * <li>from the known list of {@link org.apache.camel.spi.ThreadPoolProfile 374 * ThreadPoolProfile(s)}.</li> 375 * <li>if none found, then <tt>null</tt> is returned.</li> 376 * </ul> 377 * 378 * @param name name which is appended to the thread name, when the 379 * {@link ExecutorService} is created based 380 * on a {@link org.apache.camel.spi.ThreadPoolProfile}. 381 * @param source the source to use the thread pool 382 * @param executorServiceRef reference name of the thread pool 383 * @return the executor service, or <tt>null</tt> if none was found. 384 */ 385 public ScheduledExecutorService lookupScheduledExecutorServiceRef(String name, Object source, String executorServiceRef) { 386 387 ExecutorServiceManager manager = routeContext.getCamelContext().getExecutorServiceManager(); 388 ObjectHelper.notNull(manager, "ExecutorServiceManager", routeContext.getCamelContext()); 389 ObjectHelper.notNull(executorServiceRef, "executorServiceRef"); 390 391 // lookup in registry first and use existing thread pool if exists 392 ScheduledExecutorService answer = routeContext.lookup(executorServiceRef, ScheduledExecutorService.class); 393 if (answer == null) { 394 // then create a thread pool assuming the ref is a thread pool 395 // profile id 396 answer = manager.newScheduledThreadPool(source, name, executorServiceRef); 397 } 398 return answer; 399 } 400 401 /** 402 * Will lookup in {@link org.apache.camel.spi.Registry} for a 403 * {@link ExecutorService} registered with the given 404 * <tt>executorServiceRef</tt> name. 405 * <p/> 406 * This method will lookup for configured thread pool in the following order 407 * <ul> 408 * <li>from the {@link org.apache.camel.spi.Registry} if found</li> 409 * <li>from the known list of {@link org.apache.camel.spi.ThreadPoolProfile 410 * ThreadPoolProfile(s)}.</li> 411 * <li>if none found, then <tt>null</tt> is returned.</li> 412 * </ul> 413 * 414 * @param name name which is appended to the thread name, when the 415 * {@link ExecutorService} is created based 416 * on a {@link org.apache.camel.spi.ThreadPoolProfile}. 417 * @param source the source to use the thread pool 418 * @param executorServiceRef reference name of the thread pool 419 * @return the executor service, or <tt>null</tt> if none was found. 420 */ 421 public ExecutorService lookupExecutorServiceRef(String name, Object source, String executorServiceRef) { 422 423 ExecutorServiceManager manager = camelContext.getExecutorServiceManager(); 424 ObjectHelper.notNull(manager, "ExecutorServiceManager", camelContext); 425 ObjectHelper.notNull(executorServiceRef, "executorServiceRef"); 426 427 // lookup in registry first and use existing thread pool if exists 428 ExecutorService answer = routeContext.lookup(executorServiceRef, ExecutorService.class); 429 if (answer == null) { 430 // then create a thread pool assuming the ref is a thread pool 431 // profile id 432 answer = manager.newThreadPool(source, name, executorServiceRef); 433 } 434 return answer; 435 } 436 437 /** 438 * Is there any outputs in the given list. 439 * <p/> 440 * Is used for check if the route output has any real outputs (non 441 * abstracts) 442 * 443 * @param outputs the outputs 444 * @param excludeAbstract whether or not to exclude abstract outputs (e.g. 445 * skip onException etc.) 446 * @return <tt>true</tt> if has outputs, otherwise <tt>false</tt> is 447 * returned 448 */ 449 @SuppressWarnings({"unchecked", "rawtypes"}) 450 public boolean hasOutputs(List<ProcessorDefinition<?>> outputs, boolean excludeAbstract) { 451 if (outputs == null || outputs.isEmpty()) { 452 return false; 453 } 454 if (!excludeAbstract) { 455 return true; 456 } 457 for (ProcessorDefinition output : outputs) { 458 if (output.isWrappingEntireOutput()) { 459 // special for those as they wrap entire output, so we should 460 // just check its output 461 return hasOutputs(output.getOutputs(), excludeAbstract); 462 } 463 if (!output.isAbstract()) { 464 return true; 465 } 466 } 467 return false; 468 } 469 470 /** 471 * Override this in definition class and implement logic to create the 472 * processor based on the definition model. 473 */ 474 public abstract Processor createProcessor() throws Exception; 475 476 /** 477 * Prefer to use {#link #createChildProcessor}. 478 */ 479 protected Processor createOutputsProcessor() throws Exception { 480 Collection<ProcessorDefinition<?>> outputs = definition.getOutputs(); 481 return createOutputsProcessor(outputs); 482 } 483 484 /** 485 * Creates the child processor (outputs) from the current definition 486 * 487 * @param mandatory whether or not children is mandatory (ie the definition 488 * should have outputs) 489 * @return the created children, or <tt>null</tt> if definition had no 490 * output 491 * @throws Exception is thrown if error creating the child or if it was 492 * mandatory and there was no output defined on definition 493 */ 494 protected Processor createChildProcessor(boolean mandatory) throws Exception { 495 Processor children = null; 496 // at first use custom factory 497 if (camelContext.adapt(ExtendedCamelContext.class).getProcessorFactory() != null) { 498 children = camelContext.adapt(ExtendedCamelContext.class).getProcessorFactory().createChildProcessor(routeContext, definition, mandatory); 499 } 500 // fallback to default implementation if factory did not create the 501 // child 502 if (children == null) { 503 children = createOutputsProcessor(); 504 } 505 506 if (children == null && mandatory) { 507 throw new IllegalArgumentException("Definition has no children on " + definition); 508 } 509 return children; 510 } 511 512 public void addRoutes() throws Exception { 513 Channel processor = makeProcessor(); 514 if (processor == null) { 515 // no processor to add 516 return; 517 } 518 519 if (!routeContext.isRouteAdded()) { 520 // are we routing to an endpoint interceptor, if so we should not 521 // add it as an event driven 522 // processor as we use the producer to trigger the interceptor 523 boolean endpointInterceptor = processor.getNextProcessor() instanceof InterceptEndpointProcessor; 524 525 // only add regular processors as event driven 526 if (endpointInterceptor) { 527 log.debug("Endpoint interceptor should not be added as an event driven consumer route: {}", processor); 528 } else { 529 log.trace("Adding event driven processor: {}", processor); 530 routeContext.addEventDrivenProcessor(processor); 531 } 532 } 533 } 534 535 /** 536 * Wraps the child processor in whatever necessary interceptors and error 537 * handlers 538 */ 539 public Channel wrapProcessor(Processor processor) throws Exception { 540 // don't double wrap 541 if (processor instanceof Channel) { 542 return (Channel)processor; 543 } 544 return wrapChannel(processor, null); 545 } 546 547 protected Channel wrapChannel(Processor processor, ProcessorDefinition<?> child) throws Exception { 548 return wrapChannel(processor, child, definition.isInheritErrorHandler()); 549 } 550 551 protected Channel wrapChannel(Processor processor, ProcessorDefinition<?> child, Boolean inheritErrorHandler) throws Exception { 552 // put a channel in between this and each output to control the route 553 // flow logic 554 DefaultChannel channel = new DefaultChannel(camelContext); 555 556 // add interceptor strategies to the channel must be in this order: 557 // camel context, route context, local 558 List<InterceptStrategy> interceptors = new ArrayList<>(); 559 addInterceptStrategies(interceptors, camelContext.adapt(ExtendedCamelContext.class).getInterceptStrategies()); 560 addInterceptStrategies(interceptors, routeContext.getInterceptStrategies()); 561 addInterceptStrategies(interceptors, definition.getInterceptStrategies()); 562 563 // force the creation of an id 564 RouteDefinitionHelper.forceAssignIds(camelContext, definition); 565 566 // fix parent/child relationship. This will be the case of the routes 567 // has been 568 // defined using XML DSL or end user may have manually assembled a route 569 // from the model. 570 // Background note: parent/child relationship is assembled on-the-fly 571 // when using Java DSL (fluent builders) 572 // where as when using XML DSL (JAXB) then it fixed after, but if people 573 // are using custom interceptors 574 // then we need to fix the parent/child relationship beforehand, and 575 // thus we can do it here 576 // ideally we need the design time route -> runtime route to be a 577 // 2-phase pass (scheduled work for Camel 3.0) 578 if (child != null && definition != child) { 579 child.setParent(definition); 580 } 581 582 // set the child before init the channel 583 RouteDefinition route = ProcessorDefinitionHelper.getRoute(definition); 584 boolean first = false; 585 if (route != null && !route.getOutputs().isEmpty()) { 586 first = route.getOutputs().get(0) == definition; 587 } 588 // set scoping 589 boolean routeScoped = true; 590 if (definition instanceof OnExceptionDefinition) { 591 routeScoped = ((OnExceptionDefinition)definition).isRouteScoped(); 592 } else if (this.definition instanceof OnCompletionDefinition) { 593 routeScoped = ((OnCompletionDefinition)definition).isRouteScoped(); 594 } 595 // initialize the channel 596 channel.initChannel(routeContext, definition, child, interceptors, processor, route, first, routeScoped); 597 598 boolean wrap = false; 599 // set the error handler, must be done after init as we can set the 600 // error handler as first in the chain 601 if (definition instanceof TryDefinition || definition instanceof CatchDefinition || definition instanceof FinallyDefinition) { 602 // do not use error handler for try .. catch .. finally blocks as it 603 // will handle errors itself 604 log.trace("{} is part of doTry .. doCatch .. doFinally so no error handler is applied", definition); 605 } else if (ProcessorDefinitionHelper.isParentOfType(TryDefinition.class, definition, true) 606 || ProcessorDefinitionHelper.isParentOfType(CatchDefinition.class, definition, true) 607 || ProcessorDefinitionHelper.isParentOfType(FinallyDefinition.class, definition, true)) { 608 // do not use error handler for try .. catch .. finally blocks as it 609 // will handle errors itself 610 // by checking that any of our parent(s) is not a try .. catch or 611 // finally type 612 log.trace("{} is part of doTry .. doCatch .. doFinally so no error handler is applied", definition); 613 } else if (definition instanceof OnExceptionDefinition || ProcessorDefinitionHelper.isParentOfType(OnExceptionDefinition.class, definition, true)) { 614 log.trace("{} is part of OnException so no error handler is applied", definition); 615 // do not use error handler for onExceptions blocks as it will 616 // handle errors itself 617 } else if (definition instanceof CircuitBreakerDefinition || ProcessorDefinitionHelper.isParentOfType(CircuitBreakerDefinition.class, definition, true)) { 618 // do not use error handler for circuit breaker 619 // however if inherit error handler is enabled, we need to wrap an error handler on the parent 620 if (inheritErrorHandler != null && inheritErrorHandler && child == null) { 621 // only wrap the parent (not the children of the circuit breaker) 622 wrap = true; 623 } else { 624 log.trace("{} is part of CircuitBreaker so no error handler is applied", definition); 625 } 626 } else if (definition instanceof MulticastDefinition) { 627 // do not use error handler for multicast as it offers fine grained 628 // error handlers for its outputs 629 // however if share unit of work is enabled, we need to wrap an 630 // error handler on the multicast parent 631 MulticastDefinition def = (MulticastDefinition)definition; 632 boolean isShareUnitOfWork = parseBoolean(def.getShareUnitOfWork(), false); 633 if (isShareUnitOfWork && child == null) { 634 // only wrap the parent (not the children of the multicast) 635 wrap = true; 636 } else { 637 log.trace("{} is part of multicast which have special error handling so no error handler is applied", definition); 638 } 639 } else { 640 // use error handler by default or if configured to do so 641 wrap = true; 642 } 643 if (wrap) { 644 wrapChannelInErrorHandler(channel, inheritErrorHandler); 645 } 646 647 // do post init at the end 648 channel.postInitChannel(); 649 log.trace("{} wrapped in Channel: {}", definition, channel); 650 651 return channel; 652 } 653 654 /** 655 * Wraps the given channel in error handler (if error handler is inherited) 656 * 657 * @param channel the channel 658 * @param inheritErrorHandler whether to inherit error handler 659 * @throws Exception can be thrown if failed to create error handler builder 660 */ 661 private void wrapChannelInErrorHandler(DefaultChannel channel, Boolean inheritErrorHandler) throws Exception { 662 if (inheritErrorHandler == null || inheritErrorHandler) { 663 log.trace("{} is configured to inheritErrorHandler", definition); 664 Processor output = channel.getOutput(); 665 Processor errorHandler = wrapInErrorHandler(output); 666 // set error handler on channel 667 channel.setErrorHandler(errorHandler); 668 } else { 669 log.debug("{} is configured to not inheritErrorHandler.", definition); 670 } 671 } 672 673 /** 674 * Wraps the given output in an error handler 675 * 676 * @param output the output 677 * @return the output wrapped with the error handler 678 * @throws Exception can be thrown if failed to create error handler builder 679 */ 680 protected Processor wrapInErrorHandler(Processor output) throws Exception { 681 ErrorHandlerFactory builder = routeContext.getErrorHandlerFactory(); 682 // create error handler 683 Processor errorHandler = ErrorHandlerReifier.reifier(routeContext, builder).createErrorHandler(output); 684 685 // invoke lifecycles so we can manage this error handler builder 686 for (LifecycleStrategy strategy : camelContext.getLifecycleStrategies()) { 687 strategy.onErrorHandlerAdd(routeContext, errorHandler, builder); 688 } 689 690 return errorHandler; 691 } 692 693 /** 694 * Adds the given list of interceptors to the channel. 695 * 696 * @param interceptors the list to add strategies 697 * @param strategies list of strategies to add. 698 */ 699 protected void addInterceptStrategies(List<InterceptStrategy> interceptors, List<InterceptStrategy> strategies) { 700 interceptors.addAll(strategies); 701 } 702 703 /** 704 * Creates a new instance of some kind of composite processor which defaults 705 * to using a {@link Pipeline} but derived classes could change the 706 * behaviour 707 */ 708 protected Processor createCompositeProcessor(List<Processor> list) throws Exception { 709 return Pipeline.newInstance(camelContext, list); 710 } 711 712 protected Processor createOutputsProcessor(Collection<ProcessorDefinition<?>> outputs) throws Exception { 713 return createOutputsProcessorImpl(outputs); 714 } 715 716 protected Processor createOutputsProcessorImpl(Collection<ProcessorDefinition<?>> outputs) throws Exception { 717 List<Processor> list = new ArrayList<>(); 718 for (ProcessorDefinition<?> output : outputs) { 719 720 // allow any custom logic before we create the processor 721 reifier(routeContext, output).preCreateProcessor(); 722 723 Processor processor = createProcessor(output); 724 725 // inject id 726 if (processor instanceof IdAware) { 727 String id = getId(output, routeContext); 728 ((IdAware)processor).setId(id); 729 } 730 if (processor instanceof RouteIdAware) { 731 ((RouteIdAware)processor).setRouteId(routeContext.getRouteId()); 732 } 733 734 if (output instanceof Channel && processor == null) { 735 continue; 736 } 737 738 Processor channel = wrapChannel(processor, output); 739 list.add(channel); 740 } 741 742 // if more than one output wrap than in a composite processor else just 743 // keep it as is 744 Processor processor = null; 745 if (!list.isEmpty()) { 746 if (list.size() == 1) { 747 processor = list.get(0); 748 } else { 749 processor = createCompositeProcessor(list); 750 } 751 } 752 753 return processor; 754 } 755 756 protected Processor createProcessor(ProcessorDefinition<?> output) throws Exception { 757 Processor processor = null; 758 // at first use custom factory 759 if (camelContext.adapt(ExtendedCamelContext.class).getProcessorFactory() != null) { 760 processor = camelContext.adapt(ExtendedCamelContext.class).getProcessorFactory().createProcessor(routeContext, output); 761 } 762 // fallback to default implementation if factory did not create the 763 // processor 764 if (processor == null) { 765 processor = reifier(routeContext, output).createProcessor(); 766 } 767 return processor; 768 } 769 770 /** 771 * Creates the processor and wraps it in any necessary interceptors and 772 * error handlers 773 */ 774 protected Channel makeProcessor() throws Exception { 775 Processor processor = null; 776 777 // allow any custom logic before we create the processor 778 preCreateProcessor(); 779 780 // at first use custom factory 781 if (camelContext.adapt(ExtendedCamelContext.class).getProcessorFactory() != null) { 782 processor = camelContext.adapt(ExtendedCamelContext.class).getProcessorFactory().createProcessor(routeContext, definition); 783 } 784 // fallback to default implementation if factory did not create the 785 // processor 786 if (processor == null) { 787 processor = createProcessor(); 788 } 789 790 // inject id 791 if (processor instanceof IdAware) { 792 String id = getId(definition, routeContext); 793 ((IdAware)processor).setId(id); 794 } 795 if (processor instanceof RouteIdAware) { 796 ((RouteIdAware)processor).setRouteId(routeContext.getRouteId()); 797 } 798 799 if (processor == null) { 800 // no processor to make 801 return null; 802 } 803 return wrapProcessor(processor); 804 } 805 806 /** 807 * Strategy to execute any custom logic before the {@link Processor} is 808 * created. 809 */ 810 protected void preCreateProcessor() { 811 definition.preCreateProcessor(); 812 } 813 814 /** 815 * Strategy for children to do any custom configuration 816 * 817 * @param output the child to be added as output to this 818 */ 819 public void configureChild(ProcessorDefinition<?> output) { 820 // noop 821 } 822 823 protected String getId(OptionalIdentifiedDefinition<?> def, RouteContext routeContext) { 824 return def.idOrCreate(camelContext.adapt(ExtendedCamelContext.class).getNodeIdFactory()); 825 } 826 827}