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 */
017 package org.apache.camel.core.xml;
018
019 import java.util.ArrayList;
020 import java.util.HashSet;
021 import java.util.List;
022 import java.util.Map;
023 import java.util.Set;
024 import javax.xml.bind.annotation.XmlAccessType;
025 import javax.xml.bind.annotation.XmlAccessorType;
026 import javax.xml.bind.annotation.XmlTransient;
027
028 import org.apache.camel.CamelContext;
029 import org.apache.camel.CamelException;
030 import org.apache.camel.RoutesBuilder;
031 import org.apache.camel.ShutdownRoute;
032 import org.apache.camel.ShutdownRunningTask;
033 import org.apache.camel.builder.ErrorHandlerBuilderRef;
034 import org.apache.camel.builder.RouteBuilder;
035 import org.apache.camel.component.properties.PropertiesComponent;
036 import org.apache.camel.component.properties.PropertiesResolver;
037 import org.apache.camel.core.xml.scan.PatternBasedPackageScanFilter;
038 import org.apache.camel.management.DefaultManagementAgent;
039 import org.apache.camel.management.DefaultManagementLifecycleStrategy;
040 import org.apache.camel.management.DefaultManagementStrategy;
041 import org.apache.camel.management.ManagedManagementStrategy;
042 import org.apache.camel.model.ContextScanDefinition;
043 import org.apache.camel.model.FromDefinition;
044 import org.apache.camel.model.IdentifiedType;
045 import org.apache.camel.model.InterceptDefinition;
046 import org.apache.camel.model.InterceptFromDefinition;
047 import org.apache.camel.model.InterceptSendToEndpointDefinition;
048 import org.apache.camel.model.OnCompletionDefinition;
049 import org.apache.camel.model.OnExceptionDefinition;
050 import org.apache.camel.model.PackageScanDefinition;
051 import org.apache.camel.model.ProcessorDefinition;
052 import org.apache.camel.model.RouteBuilderDefinition;
053 import org.apache.camel.model.RouteContainer;
054 import org.apache.camel.model.RouteContextRefDefinition;
055 import org.apache.camel.model.RouteDefinition;
056 import org.apache.camel.model.ThreadPoolProfileDefinition;
057 import org.apache.camel.model.TransactedDefinition;
058 import org.apache.camel.model.config.PropertiesDefinition;
059 import org.apache.camel.model.dataformat.DataFormatsDefinition;
060 import org.apache.camel.processor.interceptor.Delayer;
061 import org.apache.camel.processor.interceptor.HandleFault;
062 import org.apache.camel.processor.interceptor.TraceFormatter;
063 import org.apache.camel.processor.interceptor.Tracer;
064 import org.apache.camel.spi.ClassResolver;
065 import org.apache.camel.spi.Debugger;
066 import org.apache.camel.spi.EventFactory;
067 import org.apache.camel.spi.EventNotifier;
068 import org.apache.camel.spi.ExecutorServiceStrategy;
069 import org.apache.camel.spi.FactoryFinderResolver;
070 import org.apache.camel.spi.InflightRepository;
071 import org.apache.camel.spi.InterceptStrategy;
072 import org.apache.camel.spi.LifecycleStrategy;
073 import org.apache.camel.spi.ManagementStrategy;
074 import org.apache.camel.spi.PackageScanClassResolver;
075 import org.apache.camel.spi.PackageScanFilter;
076 import org.apache.camel.spi.ProcessorFactory;
077 import org.apache.camel.spi.ShutdownStrategy;
078 import org.apache.camel.spi.ThreadPoolProfile;
079 import org.apache.camel.util.CamelContextHelper;
080 import org.apache.camel.util.EndpointHelper;
081 import org.apache.camel.util.ObjectHelper;
082 import org.apache.commons.logging.Log;
083 import org.apache.commons.logging.LogFactory;
084
085 /**
086 * A factory to create and initialize a
087 * {@link CamelContext} and install routes either explicitly configured
088 * or found by searching the classpath for Java classes which extend
089 * {@link org.apache.camel.builder.RouteBuilder}.
090 *
091 * @version $Revision: 938746 $
092 */
093 @XmlAccessorType(XmlAccessType.FIELD)
094 public abstract class AbstractCamelContextFactoryBean<T extends CamelContext> extends IdentifiedType implements RouteContainer {
095 private static final Log LOG = LogFactory.getLog(AbstractCamelContextFactoryBean.class);
096
097 @XmlTransient
098 private List<RoutesBuilder> builders = new ArrayList<RoutesBuilder>();
099 @XmlTransient
100 private ClassLoader contextClassLoaderOnStart;
101
102 public AbstractCamelContextFactoryBean() {
103 // Lets keep track of the class loader for when we actually do start things up
104 contextClassLoaderOnStart = Thread.currentThread().getContextClassLoader();
105 }
106
107 public Object getObject() throws Exception {
108 return getContext();
109 }
110
111 public Class getObjectType() {
112 return CamelContext.class;
113 }
114
115 public boolean isSingleton() {
116 return true;
117 }
118
119 public ClassLoader getContextClassLoaderOnStart() {
120 return contextClassLoaderOnStart;
121 }
122
123 public void afterPropertiesSet() throws Exception {
124 if (ObjectHelper.isEmpty(getId())) {
125 throw new IllegalArgumentException("Id must be set");
126 }
127
128 if (getProperties() != null) {
129 getContext().setProperties(getProperties().asMap());
130 }
131
132 // set the resolvers first
133 PackageScanClassResolver packageResolver = getBeanForType(PackageScanClassResolver.class);
134 if (packageResolver != null) {
135 LOG.info("Using custom PackageScanClassResolver: " + packageResolver);
136 getContext().setPackageScanClassResolver(packageResolver);
137 }
138 ClassResolver classResolver = getBeanForType(ClassResolver.class);
139 if (classResolver != null) {
140 LOG.info("Using custom ClassResolver: " + classResolver);
141 getContext().setClassResolver(classResolver);
142 }
143 FactoryFinderResolver factoryFinderResolver = getBeanForType(FactoryFinderResolver.class);
144 if (factoryFinderResolver != null) {
145 LOG.info("Using custom FactoryFinderResolver: " + factoryFinderResolver);
146 getContext().setFactoryFinderResolver(factoryFinderResolver);
147 }
148 ExecutorServiceStrategy executorServiceStrategy = getBeanForType(ExecutorServiceStrategy.class);
149 if (executorServiceStrategy != null) {
150 LOG.info("Using custom ExecutorServiceStrategy: " + executorServiceStrategy);
151 getContext().setExecutorServiceStrategy(executorServiceStrategy);
152 }
153 ProcessorFactory processorFactory = getBeanForType(ProcessorFactory.class);
154 if (processorFactory != null) {
155 LOG.info("Using custom ProcessorFactory: " + processorFactory);
156 getContext().setProcessorFactory(processorFactory);
157 }
158
159 Debugger debugger = getBeanForType(Debugger.class);
160 if (debugger != null) {
161 LOG.info("Using custom Debugger: " + debugger);
162 getContext().setDebugger(debugger);
163 }
164
165 // set the custom registry if defined
166 initCustomRegistry(getContext());
167
168 // setup property placeholder so we got it as early as possible
169 initPropertyPlaceholder();
170
171 // setup JMX agent at first
172 initJMXAgent();
173
174 Tracer tracer = getBeanForType(Tracer.class);
175 if (tracer != null) {
176 // use formatter if there is a TraceFormatter bean defined
177 TraceFormatter formatter = getBeanForType(TraceFormatter.class);
178 if (formatter != null) {
179 tracer.setFormatter(formatter);
180 }
181 LOG.info("Using custom Tracer: " + tracer);
182 getContext().addInterceptStrategy(tracer);
183 }
184
185 HandleFault handleFault = getBeanForType(HandleFault.class);
186 if (handleFault != null) {
187 LOG.info("Using custom HandleFault: " + handleFault);
188 getContext().addInterceptStrategy(handleFault);
189 }
190
191 Delayer delayer = getBeanForType(Delayer.class);
192 if (delayer != null) {
193 LOG.info("Using custom Delayer: " + delayer);
194 getContext().addInterceptStrategy(delayer);
195 }
196
197 InflightRepository inflightRepository = getBeanForType(InflightRepository.class);
198 if (delayer != null) {
199 LOG.info("Using custom InflightRepository: " + inflightRepository);
200 getContext().setInflightRepository(inflightRepository);
201 }
202
203 ManagementStrategy managementStrategy = getBeanForType(ManagementStrategy.class);
204 if (managementStrategy != null) {
205 LOG.info("Using custom ManagementStrategy: " + managementStrategy);
206 getContext().setManagementStrategy(managementStrategy);
207 }
208
209 EventFactory eventFactory = getBeanForType(EventFactory.class);
210 if (eventFactory != null) {
211 LOG.info("Using custom EventFactory: " + eventFactory);
212 getContext().getManagementStrategy().setEventFactory(eventFactory);
213 }
214
215 // set the event notifier strategies if defined
216 Map<String, EventNotifier> eventNotifiers = getContext().getRegistry().lookupByType(EventNotifier.class);
217 if (eventNotifiers != null && !eventNotifiers.isEmpty()) {
218 for (String id : eventNotifiers.keySet()) {
219 EventNotifier notifier = eventNotifiers.get(id);
220 // do not add if already added, for instance a tracer that is also an InterceptStrategy class
221 if (!getContext().getManagementStrategy().getEventNotifiers().contains(notifier)) {
222 LOG.info("Using custom EventNotifier with id: " + id + " and implementation: " + notifier);
223 getContext().getManagementStrategy().addEventNotifier(notifier);
224 }
225 }
226 }
227
228 ShutdownStrategy shutdownStrategy = getBeanForType(ShutdownStrategy.class);
229 if (shutdownStrategy != null) {
230 LOG.info("Using custom ShutdownStrategy: " + shutdownStrategy);
231 getContext().setShutdownStrategy(shutdownStrategy);
232 }
233
234 // add global interceptors
235 Map<String, InterceptStrategy> interceptStrategies = getContext().getRegistry().lookupByType(InterceptStrategy.class);
236 if (interceptStrategies != null && !interceptStrategies.isEmpty()) {
237 for (String id : interceptStrategies.keySet()) {
238 InterceptStrategy strategy = interceptStrategies.get(id);
239 // do not add if already added, for instance a tracer that is also an InterceptStrategy class
240 if (!getContext().getInterceptStrategies().contains(strategy)) {
241 LOG.info("Using custom InterceptStrategy with id: " + id + " and implementation: " + strategy);
242 getContext().addInterceptStrategy(strategy);
243 }
244 }
245 }
246
247 // set the lifecycle strategy if defined
248 Map<String, LifecycleStrategy> lifecycleStrategies = getContext().getRegistry().lookupByType(LifecycleStrategy.class);
249 if (lifecycleStrategies != null && !lifecycleStrategies.isEmpty()) {
250 for (String id : lifecycleStrategies.keySet()) {
251 LifecycleStrategy strategy = lifecycleStrategies.get(id);
252 // do not add if already added, for instance a tracer that is also an InterceptStrategy class
253 if (!getContext().getLifecycleStrategies().contains(strategy)) {
254 LOG.info("Using custom LifecycleStrategy with id: " + id + " and implementation: " + strategy);
255 getContext().addLifecycleStrategy(strategy);
256 }
257 }
258 }
259
260 // set the default thread pool profile if defined
261 initThreadPoolProfiles(getContext());
262
263 // Set the application context and camelContext for the beanPostProcessor
264 initBeanPostProcessor(getContext());
265
266 initCamelContext(getContext());
267
268 // must init route refs before we prepare the routes below
269 initRouteRefs();
270
271 // do special preparation for some concepts such as interceptors and policies
272 // this is needed as JAXB does not build exactly the same model definition as Spring DSL would do
273 // using route builders. So we have here a little custom code to fix the JAXB gaps
274 for (RouteDefinition route : getRoutes()) {
275
276 // abstracts is the cross cutting concerns
277 List<ProcessorDefinition> abstracts = new ArrayList<ProcessorDefinition>();
278
279 // upper is the cross cutting concerns such as interceptors, error handlers etc
280 List<ProcessorDefinition> upper = new ArrayList<ProcessorDefinition>();
281
282 // lower is the regular route
283 List<ProcessorDefinition> lower = new ArrayList<ProcessorDefinition>();
284
285 prepareRouteForInit(route, abstracts, lower);
286
287 // interceptors should be first for the cross cutting concerns
288 initInterceptors(route, upper);
289 // then on completion
290 initOnCompletions(abstracts, upper);
291 // then transactions
292 initTransacted(abstracts, lower);
293 // then on exception
294 initOnExceptions(abstracts, upper);
295
296 // rebuild route as upper + lower
297 route.clearOutput();
298 route.getOutputs().addAll(upper);
299 route.getOutputs().addAll(lower);
300
301 // configure parents
302 initParent(route);
303 }
304
305 if (getDataFormats() != null) {
306 getContext().setDataFormats(getDataFormats().asMap());
307 }
308
309 // lets force any lazy creation
310 getContext().addRouteDefinitions(getRoutes());
311
312 if (LOG.isDebugEnabled()) {
313 LOG.debug("Found JAXB created routes: " + getRoutes());
314 }
315 findRouteBuilders();
316 installRoutes();
317 }
318
319 protected abstract void initCustomRegistry(T context);
320
321 private void prepareRouteForInit(RouteDefinition route, List<ProcessorDefinition> abstracts,
322 List<ProcessorDefinition> lower) {
323 // filter the route into abstracts and lower
324 for (ProcessorDefinition output : route.getOutputs()) {
325 if (output.isAbstract()) {
326 abstracts.add(output);
327 } else {
328 lower.add(output);
329 }
330 }
331 }
332
333 private void initParent(RouteDefinition route) {
334 for (ProcessorDefinition output : route.getOutputs()) {
335 output.setParent(route);
336 if (output.getOutputs() != null) {
337 // recursive the outputs
338 initParent(output);
339 }
340 }
341 }
342
343 @SuppressWarnings("unchecked")
344 private void initParent(ProcessorDefinition parent) {
345 List<ProcessorDefinition> children = parent.getOutputs();
346 for (ProcessorDefinition child : children) {
347 child.setParent(parent);
348 if (child.getOutputs() != null) {
349 // recursive the children
350 initParent(child);
351 }
352 }
353 }
354
355 private void initOnExceptions(List<ProcessorDefinition> abstracts, List<ProcessorDefinition> upper) {
356 // add global on exceptions if any
357 List<OnExceptionDefinition> onExceptions = getOnExceptions();
358 if (onExceptions != null && !onExceptions.isEmpty()) {
359 abstracts.addAll(onExceptions);
360 }
361
362 // now add onExceptions to the route
363 for (ProcessorDefinition output : abstracts) {
364 if (output instanceof OnExceptionDefinition) {
365 // on exceptions must be added at top, so the route flow is correct as
366 // on exceptions should be the first outputs
367 upper.add(0, output);
368 }
369 }
370 }
371
372 private void initInterceptors(RouteDefinition route, List<ProcessorDefinition> upper) {
373 // configure intercept
374 for (InterceptDefinition intercept : getIntercepts()) {
375 intercept.afterPropertiesSet();
376 // add as first output so intercept is handled before the actual route and that gives
377 // us the needed head start to init and be able to intercept all the remaining processing steps
378 upper.add(0, intercept);
379 }
380
381 // configure intercept from
382 for (InterceptFromDefinition intercept : getInterceptFroms()) {
383
384 // should we only apply interceptor for a given endpoint uri
385 boolean match = true;
386 if (intercept.getUri() != null) {
387 match = false;
388 for (FromDefinition input : route.getInputs()) {
389 if (EndpointHelper.matchEndpoint(input.getUri(), intercept.getUri())) {
390 match = true;
391 break;
392 }
393 }
394 }
395
396 if (match) {
397 intercept.afterPropertiesSet();
398 // add as first output so intercept is handled before the actual route and that gives
399 // us the needed head start to init and be able to intercept all the remaining processing steps
400 upper.add(0, intercept);
401 }
402 }
403
404 // configure intercept send to endpoint
405 for (InterceptSendToEndpointDefinition intercept : getInterceptSendToEndpoints()) {
406 intercept.afterPropertiesSet();
407 // add as first output so intercept is handled before the actual route and that gives
408 // us the needed head start to init and be able to intercept all the remaining processing steps
409 upper.add(0, intercept);
410 }
411 }
412
413 private void initOnCompletions(List<ProcessorDefinition> abstracts, List<ProcessorDefinition> upper) {
414 List<OnCompletionDefinition> completions = new ArrayList<OnCompletionDefinition>();
415
416 // find the route scoped onCompletions
417 for (ProcessorDefinition out : abstracts) {
418 if (out instanceof OnCompletionDefinition) {
419 completions.add((OnCompletionDefinition) out);
420 }
421 }
422
423 // only add global onCompletion if there are no route already
424 if (completions.isEmpty()) {
425 completions = getOnCompletions();
426 }
427
428 // are there any completions to init at all?
429 if (completions.isEmpty()) {
430 return;
431 }
432
433 upper.addAll(completions);
434 }
435
436 private void initTransacted(List<ProcessorDefinition> abstracts, List<ProcessorDefinition> lower) {
437 TransactedDefinition transacted = null;
438
439 // add to correct type
440 for (ProcessorDefinition type : abstracts) {
441 if (type instanceof TransactedDefinition) {
442 if (transacted == null) {
443 transacted = (TransactedDefinition) type;
444 } else {
445 throw new IllegalArgumentException("The route can only have one transacted defined");
446 }
447 }
448 }
449
450 if (transacted != null) {
451 // the outputs should be moved to the transacted policy
452 transacted.getOutputs().addAll(lower);
453 // and add it as the single output
454 lower.clear();
455 lower.add(transacted);
456 }
457 }
458
459 private void initJMXAgent() throws Exception {
460 CamelJMXAgentDefinition camelJMXAgent = getCamelJMXAgent();
461 if (camelJMXAgent != null && camelJMXAgent.isAgentDisabled()) {
462 LOG.info("JMXAgent disabled");
463 // clear the existing lifecycle strategies define by the DefaultCamelContext constructor
464 getContext().getLifecycleStrategies().clear();
465 // no need to add a lifecycle strategy as we do not need one as JMX is disabled
466 getContext().setManagementStrategy(new DefaultManagementStrategy());
467 } else if (camelJMXAgent != null) {
468 LOG.info("JMXAgent enabled: " + camelJMXAgent);
469 DefaultManagementAgent agent = new DefaultManagementAgent(getContext());
470 agent.setConnectorPort(parseInteger(camelJMXAgent.getConnectorPort()));
471 agent.setCreateConnector(parseBoolean(camelJMXAgent.getCreateConnector()));
472 agent.setMBeanObjectDomainName(parseText(camelJMXAgent.getMbeanObjectDomainName()));
473 agent.setMBeanServerDefaultDomain(parseText(camelJMXAgent.getMbeanServerDefaultDomain()));
474 agent.setRegistryPort(parseInteger(camelJMXAgent.getRegistryPort()));
475 agent.setServiceUrlPath(parseText(camelJMXAgent.getServiceUrlPath()));
476 agent.setUsePlatformMBeanServer(parseBoolean(camelJMXAgent.getUsePlatformMBeanServer()));
477 agent.setOnlyRegisterProcessorWithCustomId(parseBoolean(camelJMXAgent.getOnlyRegisterProcessorWithCustomId()));
478
479 ManagementStrategy managementStrategy = new ManagedManagementStrategy(agent);
480 getContext().setManagementStrategy(managementStrategy);
481
482 // clear the existing lifecycle strategies define by the DefaultCamelContext constructor
483 getContext().getLifecycleStrategies().clear();
484 getContext().addLifecycleStrategy(new DefaultManagementLifecycleStrategy(getContext()));
485 // set additional configuration from camelJMXAgent
486 boolean onlyId = agent.getOnlyRegisterProcessorWithCustomId() != null && agent.getOnlyRegisterProcessorWithCustomId();
487 getContext().getManagementStrategy().onlyManageProcessorWithCustomId(onlyId);
488 getContext().getManagementStrategy().setStatisticsLevel(camelJMXAgent.getStatisticsLevel());
489 }
490 }
491
492 private void initPropertyPlaceholder() throws Exception {
493 if (getCamelPropertyPlaceholder() != null) {
494 CamelPropertyPlaceholderDefinition def = getCamelPropertyPlaceholder();
495
496 PropertiesComponent pc = new PropertiesComponent();
497 pc.setLocation(def.getLocation());
498
499 // if using a custom resolver
500 if (ObjectHelper.isNotEmpty(def.getPropertiesResolverRef())) {
501 PropertiesResolver resolver = CamelContextHelper.mandatoryLookup(getContext(), def.getPropertiesResolverRef(),
502 PropertiesResolver.class);
503 pc.setPropertiesResolver(resolver);
504 }
505
506 // register the properties component
507 getContext().addComponent("properties", pc);
508 }
509 }
510
511 private void initRouteRefs() throws Exception {
512 // add route refs to existing routes
513 if (getRouteRefs() != null) {
514 for (RouteContextRefDefinition ref : getRouteRefs()) {
515 List<RouteDefinition> defs = ref.lookupRoutes(getContext());
516 for (RouteDefinition def : defs) {
517 if (LOG.isDebugEnabled()) {
518 LOG.debug("Adding route from " + ref + " -> " + def);
519 }
520 // add in top as they are most likely to be common/shared
521 // which you may want to start first
522 getRoutes().add(0, def);
523 }
524 }
525 }
526 }
527
528 protected abstract <S> S getBeanForType(Class<S> clazz);
529
530 public void destroy() throws Exception {
531 getContext().stop();
532 }
533
534 private String parseText(String text) throws Exception {
535 // ensure we support property placeholders
536 return getContext().resolvePropertyPlaceholders(text);
537 }
538
539 private Integer parseInteger(String text) throws Exception {
540 // ensure we support property placeholders
541 String s = getContext().resolvePropertyPlaceholders(text);
542 if (s != null) {
543 try {
544 return new Integer(s);
545 } catch (NumberFormatException e) {
546 if (s.equals(text)) {
547 throw new IllegalArgumentException("Error parsing [" + s + "] as an Integer.", e);
548 } else {
549 throw new IllegalArgumentException("Error parsing [" + s + "] from property " + text + " as an Integer.", e);
550 }
551 }
552 }
553 return null;
554 }
555
556 private Long parseLong(String text) throws Exception {
557 // ensure we support property placeholders
558 String s = getContext().resolvePropertyPlaceholders(text);
559 if (s != null) {
560 try {
561 return new Long(s);
562 } catch (NumberFormatException e) {
563 if (s.equals(text)) {
564 throw new IllegalArgumentException("Error parsing [" + s + "] as a Long.", e);
565 } else {
566 throw new IllegalArgumentException("Error parsing [" + s + "] from property " + text + " as a Long.", e);
567 }
568 }
569 }
570 return null;
571 }
572
573 private Boolean parseBoolean(String text) throws Exception {
574 // ensure we support property placeholders
575 String s = getContext().resolvePropertyPlaceholders(text);
576 if (s != null) {
577 s = s.trim().toLowerCase();
578 if (s.equals("true") || s.equals("false")) {
579 return new Boolean(s);
580 } else {
581 if (s.equals(text)) {
582 throw new IllegalArgumentException("Error parsing [" + s + "] as a Boolean.");
583 } else {
584 throw new IllegalArgumentException("Error parsing [" + s + "] from property " + text + " as a Boolean.");
585 }
586 }
587 }
588 return null;
589 }
590
591 // Properties
592 // -------------------------------------------------------------------------
593 public T getContext() {
594 return getContext(true);
595 }
596
597 public abstract T getContext(boolean create);
598
599 public abstract List<RouteDefinition> getRoutes();
600
601 public abstract List<InterceptDefinition> getIntercepts();
602
603 public abstract List<InterceptFromDefinition> getInterceptFroms();
604
605 public abstract List<InterceptSendToEndpointDefinition> getInterceptSendToEndpoints();
606
607 public abstract PropertiesDefinition getProperties();
608
609 public abstract String[] getPackages();
610
611 public abstract PackageScanDefinition getPackageScan();
612
613 public abstract void setPackageScan(PackageScanDefinition packageScan);
614
615 public abstract ContextScanDefinition getContextScan();
616
617 public abstract void setContextScan(ContextScanDefinition contextScan);
618
619 public abstract CamelPropertyPlaceholderDefinition getCamelPropertyPlaceholder();
620
621 public abstract String getTrace();
622
623 public abstract String getStreamCache();
624
625 public abstract String getDelayer();
626
627 public abstract String getHandleFault();
628
629 public abstract String getAutoStartup();
630
631 public abstract CamelJMXAgentDefinition getCamelJMXAgent();
632
633 public abstract List<RouteBuilderDefinition> getBuilderRefs();
634
635 public abstract List<RouteContextRefDefinition> getRouteRefs();
636
637 public abstract String getErrorHandlerRef();
638
639 public abstract DataFormatsDefinition getDataFormats();
640
641 public abstract List<OnExceptionDefinition> getOnExceptions();
642
643 public abstract List<OnCompletionDefinition> getOnCompletions();
644
645 public abstract ShutdownRoute getShutdownRoute();
646
647 public abstract ShutdownRunningTask getShutdownRunningTask();
648
649 public abstract List<ThreadPoolProfileDefinition> getThreadPoolProfiles();
650
651 public abstract String getDependsOn();
652
653 // Implementation methods
654 // -------------------------------------------------------------------------
655
656 /**
657 * Initializes the context
658 *
659 * @param ctx the context
660 * @throws Exception is thrown if error occurred
661 */
662 protected void initCamelContext(T ctx) throws Exception {
663 if (getStreamCache() != null) {
664 ctx.setStreamCaching(parseBoolean(getStreamCache()));
665 }
666 if (getTrace() != null) {
667 ctx.setTracing(parseBoolean(getTrace()));
668 }
669 if (getDelayer() != null) {
670 ctx.setDelayer(parseLong(getDelayer()));
671 }
672 if (getHandleFault() != null) {
673 ctx.setHandleFault(parseBoolean(getHandleFault()));
674 }
675 if (getErrorHandlerRef() != null) {
676 ctx.setErrorHandlerBuilder(new ErrorHandlerBuilderRef(getErrorHandlerRef()));
677 }
678 if (getAutoStartup() != null) {
679 ctx.setAutoStartup(parseBoolean(getAutoStartup()));
680 }
681 if (getShutdownRoute() != null) {
682 ctx.setShutdownRoute(getShutdownRoute());
683 }
684 if (getShutdownRunningTask() != null) {
685 ctx.setShutdownRunningTask(getShutdownRunningTask());
686 }
687 }
688
689 private void initThreadPoolProfiles(T context) {
690 Set<String> defaultIds = new HashSet<String>();
691
692 // lookup and use custom profiles from the registry
693 Map<String, ThreadPoolProfile> profiles = context.getRegistry().lookupByType(ThreadPoolProfile.class);
694 if (profiles != null && !profiles.isEmpty()) {
695 for (String id : profiles.keySet()) {
696 ThreadPoolProfile profile = profiles.get(id);
697 // do not add if already added, for instance a tracer that is also an InterceptStrategy class
698 if (profile.isDefaultProfile()) {
699 LOG.info("Using custom default ThreadPoolProfile with id: " + id + " and implementation: " + profile);
700 context.getExecutorServiceStrategy().setDefaultThreadPoolProfile(profile);
701 defaultIds.add(id);
702 } else {
703 context.getExecutorServiceStrategy().registerThreadPoolProfile(profile);
704 }
705 }
706 }
707
708 // use custom profiles defined in the CamelContext
709 if (getThreadPoolProfiles() != null && !getThreadPoolProfiles().isEmpty()) {
710 for (ThreadPoolProfileDefinition profile : getThreadPoolProfiles()) {
711 if (profile.isDefaultProfile()) {
712 LOG.info("Using custom default ThreadPoolProfile with id: " + profile.getId() + " and implementation: " + profile);
713 context.getExecutorServiceStrategy().setDefaultThreadPoolProfile(profile);
714 defaultIds.add(profile.getId());
715 } else {
716 context.getExecutorServiceStrategy().registerThreadPoolProfile(profile);
717 }
718 }
719 }
720
721 // validate at most one is defined
722 if (defaultIds.size() > 1) {
723 throw new IllegalArgumentException("Only exactly one default ThreadPoolProfile is allowed, was " + defaultIds.size() + " ids: " + defaultIds);
724 }
725 }
726
727 protected abstract void initBeanPostProcessor(T context);
728
729 /**
730 * Strategy to install all available routes into the context
731 */
732 protected void installRoutes() throws Exception {
733 List<RouteBuilder> builders = new ArrayList<RouteBuilder>();
734
735 // lets add route builders added from references
736 if (getBuilderRefs() != null) {
737 for (RouteBuilderDefinition builderRef : getBuilderRefs()) {
738 RouteBuilder builder = builderRef.createRouteBuilder(getContext());
739 if (builder != null) {
740 builders.add(builder);
741 } else {
742 // support to get the route here
743 RoutesBuilder routes = builderRef.createRoutes(getContext());
744 if (routes != null) {
745 this.builders.add(routes);
746 } else {
747 // Throw the exception that we can't find any build here
748 throw new CamelException("Cannot find any routes with this RouteBuilder reference: " + builderRef);
749 }
750 }
751 }
752 }
753
754 // install already configured routes
755 for (RoutesBuilder routeBuilder : this.builders) {
756 getContext().addRoutes(routeBuilder);
757 }
758
759 // install builders
760 for (RouteBuilder builder : builders) {
761 // Inject the annotated resource
762 postProcessBeforeInit(builder);
763 getContext().addRoutes(builder);
764 }
765 }
766
767 protected abstract void postProcessBeforeInit(RouteBuilder builder);
768
769 /**
770 * Strategy method to try find {@link org.apache.camel.builder.RouteBuilder} instances on the classpath
771 */
772 protected void findRouteBuilders() throws Exception {
773 // package scan
774 addPackageElementContentsToScanDefinition();
775 PackageScanDefinition packageScanDef = getPackageScan();
776 if (packageScanDef != null && packageScanDef.getPackages().size() > 0) {
777 // use package scan filter
778 PatternBasedPackageScanFilter filter = new PatternBasedPackageScanFilter();
779 // support property placeholders in include and exclude
780 for (String include : packageScanDef.getIncludes()) {
781 include = getContext().resolvePropertyPlaceholders(include);
782 filter.addIncludePattern(include);
783 }
784 for (String exclude : packageScanDef.getExcludes()) {
785 exclude = getContext().resolvePropertyPlaceholders(exclude);
786 filter.addExcludePattern(exclude);
787 }
788
789 String[] normalized = normalizePackages(getContext(), packageScanDef.getPackages());
790 findRouteBuildersByPackageScan(normalized, filter, builders);
791 }
792
793 // context scan
794 ContextScanDefinition contextScanDef = getContextScan();
795 if (contextScanDef != null) {
796 // use package scan filter
797 PatternBasedPackageScanFilter filter = new PatternBasedPackageScanFilter();
798 // support property placeholders in include and exclude
799 for (String include : contextScanDef.getIncludes()) {
800 include = getContext().resolvePropertyPlaceholders(include);
801 filter.addIncludePattern(include);
802 }
803 for (String exclude : contextScanDef.getExcludes()) {
804 exclude = getContext().resolvePropertyPlaceholders(exclude);
805 filter.addExcludePattern(exclude);
806 }
807 findRouteBuildersByContextScan(filter, builders);
808 }
809 }
810
811 protected abstract void findRouteBuildersByPackageScan(String[] packages, PackageScanFilter filter, List<RoutesBuilder> builders) throws Exception;
812
813 protected abstract void findRouteBuildersByContextScan(PackageScanFilter filter, List<RoutesBuilder> builders) throws Exception;
814
815 private void addPackageElementContentsToScanDefinition() {
816 PackageScanDefinition packageScanDef = getPackageScan();
817
818 if (getPackages() != null && getPackages().length > 0) {
819 if (packageScanDef == null) {
820 packageScanDef = new PackageScanDefinition();
821 setPackageScan(packageScanDef);
822 }
823
824 for (String pkg : getPackages()) {
825 packageScanDef.getPackages().add(pkg);
826 }
827 }
828 }
829
830 private String[] normalizePackages(T context, List<String> unnormalized) throws Exception {
831 List<String> packages = new ArrayList<String>();
832 for (String name : unnormalized) {
833 // it may use property placeholders
834 name = context.resolvePropertyPlaceholders(name);
835 name = ObjectHelper.normalizeClassName(name);
836 if (ObjectHelper.isNotEmpty(name)) {
837 if (LOG.isTraceEnabled()) {
838 LOG.trace("Using package: " + name + " to scan for RouteBuilder classes");
839 }
840 packages.add(name);
841 }
842 }
843 return packages.toArray(new String[packages.size()]);
844 }
845
846 }