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.spring.xml.handler;
018
019import java.lang.reflect.Method;
020import java.util.HashMap;
021import java.util.HashSet;
022import java.util.Map;
023import java.util.Set;
024
025import javax.xml.bind.Binder;
026import javax.xml.bind.JAXBContext;
027import javax.xml.bind.JAXBException;
028
029import org.w3c.dom.Document;
030import org.w3c.dom.Element;
031import org.w3c.dom.NamedNodeMap;
032import org.w3c.dom.Node;
033import org.w3c.dom.NodeList;
034
035import org.apache.camel.builder.LegacyDeadLetterChannelBuilder;
036import org.apache.camel.builder.LegacyDefaultErrorHandlerBuilder;
037import org.apache.camel.builder.LegacyNoErrorHandlerBuilder;
038import org.apache.camel.core.xml.CamelJMXAgentDefinition;
039import org.apache.camel.core.xml.CamelPropertyPlaceholderDefinition;
040import org.apache.camel.core.xml.CamelRouteControllerDefinition;
041import org.apache.camel.core.xml.CamelStreamCachingStrategyDefinition;
042import org.apache.camel.impl.engine.DefaultCamelContextNameStrategy;
043import org.apache.camel.reifier.errorhandler.ErrorHandlerReifier;
044import org.apache.camel.reifier.errorhandler.LegacyDeadLetterChannelReifier;
045import org.apache.camel.reifier.errorhandler.LegacyDefaultErrorHandlerReifier;
046import org.apache.camel.reifier.errorhandler.LegacyNoErrorHandlerReifier;
047import org.apache.camel.spi.CamelContextNameStrategy;
048import org.apache.camel.spi.NamespaceAware;
049import org.apache.camel.spring.xml.CamelBeanPostProcessor;
050import org.apache.camel.spring.xml.CamelConsumerTemplateFactoryBean;
051import org.apache.camel.spring.xml.CamelContextFactoryBean;
052import org.apache.camel.spring.xml.CamelEndpointFactoryBean;
053import org.apache.camel.spring.xml.CamelFluentProducerTemplateFactoryBean;
054import org.apache.camel.spring.xml.CamelProducerTemplateFactoryBean;
055import org.apache.camel.spring.xml.CamelRedeliveryPolicyFactoryBean;
056import org.apache.camel.spring.xml.CamelRestContextFactoryBean;
057import org.apache.camel.spring.xml.CamelRouteConfigurationContextFactoryBean;
058import org.apache.camel.spring.xml.CamelRouteContextFactoryBean;
059import org.apache.camel.spring.xml.CamelRouteTemplateContextFactoryBean;
060import org.apache.camel.spring.xml.CamelThreadPoolFactoryBean;
061import org.apache.camel.spring.xml.KeyStoreParametersFactoryBean;
062import org.apache.camel.spring.xml.SSLContextParametersFactoryBean;
063import org.apache.camel.spring.xml.SecureRandomParametersFactoryBean;
064import org.apache.camel.spring.xml.SpringModelJAXBContextFactory;
065import org.apache.camel.support.builder.Namespaces;
066import org.apache.camel.support.builder.xml.NamespacesHelper;
067import org.apache.camel.util.ObjectHelper;
068import org.apache.camel.util.StringHelper;
069import org.slf4j.Logger;
070import org.slf4j.LoggerFactory;
071import org.springframework.beans.factory.BeanCreationException;
072import org.springframework.beans.factory.BeanDefinitionStoreException;
073import org.springframework.beans.factory.config.BeanDefinition;
074import org.springframework.beans.factory.config.RuntimeBeanReference;
075import org.springframework.beans.factory.parsing.BeanComponentDefinition;
076import org.springframework.beans.factory.support.BeanDefinitionBuilder;
077import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
078import org.springframework.beans.factory.xml.ParserContext;
079
080/**
081 * Camel namespace for the spring XML configuration file.
082 */
083public class CamelNamespaceHandler extends NamespaceHandlerSupport {
084
085    static {
086        // legacy camel-spring-xml error-handling using its own model and parsers
087        ErrorHandlerReifier.registerReifier(LegacyDeadLetterChannelBuilder.class, LegacyDeadLetterChannelReifier::new);
088        ErrorHandlerReifier.registerReifier(LegacyDefaultErrorHandlerBuilder.class, LegacyDefaultErrorHandlerReifier::new);
089        ErrorHandlerReifier.registerReifier(LegacyNoErrorHandlerBuilder.class, LegacyNoErrorHandlerReifier::new);
090        // note: spring transaction error handler is registered in camel-spring
091    }
092
093    private static final String SPRING_NS = "http://camel.apache.org/schema/spring";
094    private static final Logger LOG = LoggerFactory.getLogger(CamelNamespaceHandler.class);
095    protected BeanDefinitionParser endpointParser = new EndpointDefinitionParser();
096    protected BeanDefinitionParser beanPostProcessorParser = new BeanDefinitionParser(CamelBeanPostProcessor.class, false);
097    protected Set<String> parserElementNames = new HashSet<>();
098    protected Map<String, BeanDefinitionParser> parserMap = new HashMap<>();
099
100    private JAXBContext jaxbContext;
101    private Map<String, BeanDefinition> autoRegisterMap = new HashMap<>();
102
103    /**
104     * Prepares the nodes before parsing.
105     */
106    public static void doBeforeParse(Node node) {
107        if (node.getNodeType() == Node.ELEMENT_NODE) {
108
109            // ensure namespace with versions etc is renamed to be same namespace so we can parse using this handler
110            Document doc = node.getOwnerDocument();
111            if (node.getNamespaceURI().startsWith(SPRING_NS + "/v")) {
112                doc.renameNode(node, SPRING_NS, node.getNodeName());
113            }
114
115            // remove whitespace noise from uri, xxxUri attributes, eg new lines, and tabs etc, which allows end users to format
116            // their Camel routes in more human readable format, but at runtime those attributes must be trimmed
117            // the parser removes most of the noise, but keeps double spaces in the attribute values
118            NamedNodeMap map = node.getAttributes();
119            for (int i = 0; i < map.getLength(); i++) {
120                Node att = map.item(i);
121                if (att.getNodeName().equals("uri") || att.getNodeName().endsWith("Uri")) {
122                    final String value = att.getNodeValue();
123                    String before = StringHelper.before(value, "?");
124                    String after = StringHelper.after(value, "?");
125
126                    if (before != null && after != null) {
127                        // remove all double spaces in the uri parameters
128                        String changed = after.replaceAll("\\s{2,}", "");
129                        if (!after.equals(changed)) {
130                            String newAtr = before.trim() + "?" + changed.trim();
131                            LOG.debug("Removed whitespace noise from attribute {} -> {}", value, newAtr);
132                            att.setNodeValue(newAtr);
133                        }
134                    }
135                }
136            }
137        }
138        NodeList list = node.getChildNodes();
139        for (int i = 0; i < list.getLength(); ++i) {
140            doBeforeParse(list.item(i));
141        }
142    }
143
144    @Override
145    public void init() {
146        // register routeContext parser
147        registerParser("routeConfigurationContext", new RouteConfigurationContextDefinitionParser());
148        // register routeTemplateContext parser
149        registerParser("routeTemplateContext", new RouteTemplateContextDefinitionParser());
150        // register restContext parser
151        registerParser("restContext", new RestContextDefinitionParser());
152        // register routeContext parser
153        registerParser("routeContext", new RouteContextDefinitionParser());
154        // register endpoint parser
155        registerParser("endpoint", endpointParser);
156
157        addBeanDefinitionParser("keyStoreParameters", KeyStoreParametersFactoryBean.class, true, true);
158        addBeanDefinitionParser("secureRandomParameters", SecureRandomParametersFactoryBean.class, true, true);
159        registerBeanDefinitionParser("sslContextParameters", new SSLContextParametersFactoryBeanBeanDefinitionParser());
160
161        addBeanDefinitionParser("template", CamelProducerTemplateFactoryBean.class, true, false);
162        addBeanDefinitionParser("fluentTemplate", CamelFluentProducerTemplateFactoryBean.class, true, false);
163        addBeanDefinitionParser("consumerTemplate", CamelConsumerTemplateFactoryBean.class, true, false);
164        addBeanDefinitionParser("threadPool", CamelThreadPoolFactoryBean.class, true, true);
165        addBeanDefinitionParser("redeliveryPolicyProfile", CamelRedeliveryPolicyFactoryBean.class, true, true);
166
167        // jmx agent, stream caching, service call configurations and property placeholder cannot be used outside of the camel context
168        addBeanDefinitionParser("jmxAgent", CamelJMXAgentDefinition.class, false, false);
169        addBeanDefinitionParser("streamCaching", CamelStreamCachingStrategyDefinition.class, false, false);
170        addBeanDefinitionParser("propertyPlaceholder", CamelPropertyPlaceholderDefinition.class, false, false);
171        addBeanDefinitionParser("routeController", CamelRouteControllerDefinition.class, false, false);
172
173        // error handler could be the sub element of camelContext or defined outside camelContext
174        BeanDefinitionParser errorHandlerParser = new ErrorHandlerDefinitionParser();
175        registerParser("errorHandler", errorHandlerParser);
176        parserMap.put("errorHandler", errorHandlerParser);
177
178        // camel context
179        Class<?> cl = CamelContextFactoryBean.class;
180        registerParser("camelContext", new CamelContextBeanDefinitionParser(cl));
181    }
182
183    protected void addBeanDefinitionParser(String elementName, Class<?> type, boolean register, boolean assignId) {
184        BeanDefinitionParser parser = new BeanDefinitionParser(type, assignId);
185        if (register) {
186            registerParser(elementName, parser);
187        }
188        parserMap.put(elementName, parser);
189    }
190
191    protected void registerParser(String name, org.springframework.beans.factory.xml.BeanDefinitionParser parser) {
192        parserElementNames.add(name);
193        registerBeanDefinitionParser(name, parser);
194    }
195
196    protected Object parseUsingJaxb(Element element, ParserContext parserContext, Binder<Node> binder) {
197        try {
198            return binder.unmarshal(element);
199        } catch (JAXBException e) {
200            throw new BeanDefinitionStoreException("Failed to parse JAXB element", e);
201        }
202    }
203
204    public JAXBContext getJaxbContext() throws JAXBException {
205        if (jaxbContext == null) {
206            jaxbContext = new SpringModelJAXBContextFactory().newJAXBContext();
207        }
208        return jaxbContext;
209    }
210
211    protected class SSLContextParametersFactoryBeanBeanDefinitionParser extends BeanDefinitionParser {
212
213        public SSLContextParametersFactoryBeanBeanDefinitionParser() {
214            super(SSLContextParametersFactoryBean.class, true);
215        }
216
217        @Override
218        protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
219            doBeforeParse(element);
220            super.doParse(element, builder);
221
222            // Note: prefer to use doParse from parent and postProcess; however, parseUsingJaxb requires
223            // parserContext for no apparent reason.
224            Binder<Node> binder;
225            try {
226                binder = getJaxbContext().createBinder();
227            } catch (JAXBException e) {
228                throw new BeanDefinitionStoreException("Failed to create the JAXB binder", e);
229            }
230
231            Object value = parseUsingJaxb(element, parserContext, binder);
232
233            if (value instanceof SSLContextParametersFactoryBean) {
234                SSLContextParametersFactoryBean bean = (SSLContextParametersFactoryBean) value;
235
236                builder.addPropertyValue("cipherSuites", bean.getCipherSuites());
237                builder.addPropertyValue("cipherSuitesFilter", bean.getCipherSuitesFilter());
238                builder.addPropertyValue("secureSocketProtocols", bean.getSecureSocketProtocols());
239                builder.addPropertyValue("secureSocketProtocolsFilter", bean.getSecureSocketProtocolsFilter());
240                builder.addPropertyValue("keyManagers", bean.getKeyManagers());
241                builder.addPropertyValue("trustManagers", bean.getTrustManagers());
242                builder.addPropertyValue("secureRandom", bean.getSecureRandom());
243
244                builder.addPropertyValue("clientParameters", bean.getClientParameters());
245                builder.addPropertyValue("serverParameters", bean.getServerParameters());
246            } else {
247                throw new BeanDefinitionStoreException(
248                        "Parsed type is not of the expected type. Expected "
249                                                       + SSLContextParametersFactoryBean.class.getName() + " but found "
250                                                       + value.getClass().getName());
251            }
252        }
253    }
254
255    protected class RouteContextDefinitionParser extends BeanDefinitionParser {
256
257        public RouteContextDefinitionParser() {
258            super(CamelRouteContextFactoryBean.class, false);
259        }
260
261        @Override
262        protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
263            doBeforeParse(element);
264            super.doParse(element, parserContext, builder);
265
266            // now lets parse the routes with JAXB
267            Binder<Node> binder;
268            try {
269                binder = getJaxbContext().createBinder();
270            } catch (JAXBException e) {
271                throw new BeanDefinitionStoreException("Failed to create the JAXB binder", e);
272            }
273            Object value = parseUsingJaxb(element, parserContext, binder);
274
275            if (value instanceof CamelRouteContextFactoryBean) {
276                CamelRouteContextFactoryBean factoryBean = (CamelRouteContextFactoryBean) value;
277                builder.addPropertyValue("routes", factoryBean.getRoutes());
278            }
279
280            // lets inject the namespaces into any namespace aware POJOs
281            injectNamespaces(element, binder);
282        }
283    }
284
285    protected class RouteConfigurationContextDefinitionParser extends BeanDefinitionParser {
286
287        public RouteConfigurationContextDefinitionParser() {
288            super(CamelRouteConfigurationContextFactoryBean.class, false);
289        }
290
291        @Override
292        protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
293            doBeforeParse(element);
294            super.doParse(element, parserContext, builder);
295
296            // now lets parse the routes with JAXB
297            Binder<Node> binder;
298            try {
299                binder = getJaxbContext().createBinder();
300            } catch (JAXBException e) {
301                throw new BeanDefinitionStoreException("Failed to create the JAXB binder", e);
302            }
303            Object value = parseUsingJaxb(element, parserContext, binder);
304
305            if (value instanceof CamelRouteConfigurationContextFactoryBean) {
306                CamelRouteConfigurationContextFactoryBean factoryBean = (CamelRouteConfigurationContextFactoryBean) value;
307                builder.addPropertyValue("routeConfigurations", factoryBean.getRouteConfigurations());
308            }
309
310            // lets inject the namespaces into any namespace aware POJOs
311            injectNamespaces(element, binder);
312        }
313    }
314
315    protected class RouteTemplateContextDefinitionParser extends BeanDefinitionParser {
316
317        public RouteTemplateContextDefinitionParser() {
318            super(CamelRouteTemplateContextFactoryBean.class, false);
319        }
320
321        @Override
322        protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
323            doBeforeParse(element);
324            super.doParse(element, parserContext, builder);
325
326            // now lets parse the routes with JAXB
327            Binder<Node> binder;
328            try {
329                binder = getJaxbContext().createBinder();
330            } catch (JAXBException e) {
331                throw new BeanDefinitionStoreException("Failed to create the JAXB binder", e);
332            }
333            Object value = parseUsingJaxb(element, parserContext, binder);
334
335            if (value instanceof CamelRouteTemplateContextFactoryBean) {
336                CamelRouteTemplateContextFactoryBean factoryBean = (CamelRouteTemplateContextFactoryBean) value;
337                builder.addPropertyValue("routeTemplates", factoryBean.getRouteTemplates());
338            }
339
340            // lets inject the namespaces into any namespace aware POJOs
341            injectNamespaces(element, binder);
342        }
343    }
344
345    protected class EndpointDefinitionParser extends BeanDefinitionParser {
346
347        public EndpointDefinitionParser() {
348            super(CamelEndpointFactoryBean.class, false);
349        }
350
351        @Override
352        protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
353            doBeforeParse(element);
354            super.doParse(element, parserContext, builder);
355
356            // now lets parse the routes with JAXB
357            Binder<Node> binder;
358            try {
359                binder = getJaxbContext().createBinder();
360            } catch (JAXBException e) {
361                throw new BeanDefinitionStoreException("Failed to create the JAXB binder", e);
362            }
363            Object value = parseUsingJaxb(element, parserContext, binder);
364
365            if (value instanceof CamelEndpointFactoryBean) {
366                CamelEndpointFactoryBean factoryBean = (CamelEndpointFactoryBean) value;
367                builder.addPropertyValue("properties", factoryBean.getProperties());
368            }
369        }
370    }
371
372    protected class RestContextDefinitionParser extends BeanDefinitionParser {
373
374        public RestContextDefinitionParser() {
375            super(CamelRestContextFactoryBean.class, false);
376        }
377
378        @Override
379        protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
380            doBeforeParse(element);
381            super.doParse(element, parserContext, builder);
382
383            // now lets parse the routes with JAXB
384            Binder<Node> binder;
385            try {
386                binder = getJaxbContext().createBinder();
387            } catch (JAXBException e) {
388                throw new BeanDefinitionStoreException("Failed to create the JAXB binder", e);
389            }
390            Object value = parseUsingJaxb(element, parserContext, binder);
391
392            if (value instanceof CamelRestContextFactoryBean) {
393                CamelRestContextFactoryBean factoryBean = (CamelRestContextFactoryBean) value;
394                builder.addPropertyValue("rests", factoryBean.getRests());
395            }
396
397            // lets inject the namespaces into any namespace aware POJOs
398            injectNamespaces(element, binder);
399        }
400    }
401
402    protected class CamelContextBeanDefinitionParser extends BeanDefinitionParser {
403
404        public CamelContextBeanDefinitionParser(Class<?> type) {
405            super(type, false);
406        }
407
408        @Override
409        protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
410            doBeforeParse(element);
411            super.doParse(element, parserContext, builder);
412
413            String contextId = element.getAttribute("id");
414            boolean implicitId = false;
415
416            // lets avoid folks having to explicitly give an ID to a camel context
417            if (ObjectHelper.isEmpty(contextId)) {
418                // if no explicit id was set then use a default auto generated name
419                CamelContextNameStrategy strategy = new DefaultCamelContextNameStrategy();
420                contextId = strategy.getName();
421                element.setAttributeNS(null, "id", contextId);
422                implicitId = true;
423            }
424
425            // now lets parse the routes with JAXB
426            Binder<Node> binder;
427            try {
428                binder = getJaxbContext().createBinder();
429            } catch (JAXBException e) {
430                throw new BeanDefinitionStoreException("Failed to create the JAXB binder", e);
431            }
432            Object value = parseUsingJaxb(element, parserContext, binder);
433
434            CamelContextFactoryBean factoryBean = null;
435            if (value instanceof CamelContextFactoryBean) {
436                // set the property value with the JAXB parsed value
437                factoryBean = (CamelContextFactoryBean) value;
438                builder.addPropertyValue("id", contextId);
439                builder.addPropertyValue("implicitId", implicitId);
440                builder.addPropertyValue("restConfiguration", factoryBean.getRestConfiguration());
441                builder.addPropertyValue("rests", factoryBean.getRests());
442                builder.addPropertyValue("routeConfigurations", factoryBean.getRouteConfigurations());
443                builder.addPropertyValue("routeTemplates", factoryBean.getRouteTemplates());
444                builder.addPropertyValue("templatedRoutes", factoryBean.getTemplatedRoutes());
445                builder.addPropertyValue("routes", factoryBean.getRoutes());
446                builder.addPropertyValue("intercepts", factoryBean.getIntercepts());
447                builder.addPropertyValue("interceptFroms", factoryBean.getInterceptFroms());
448                builder.addPropertyValue("interceptSendToEndpoints", factoryBean.getInterceptSendToEndpoints());
449                builder.addPropertyValue("dataFormats", factoryBean.getDataFormats());
450                builder.addPropertyValue("transformers", factoryBean.getTransformers());
451                builder.addPropertyValue("validators", factoryBean.getValidators());
452                builder.addPropertyValue("onCompletions", factoryBean.getOnCompletions());
453                builder.addPropertyValue("onExceptions", factoryBean.getOnExceptions());
454                builder.addPropertyValue("routeConfigurationRefs", factoryBean.getRouteConfigurationRefs());
455                builder.addPropertyValue("routeTemplateRefs", factoryBean.getRouteTemplateRefs());
456                builder.addPropertyValue("builderRefs", factoryBean.getBuilderRefs());
457                builder.addPropertyValue("routeRefs", factoryBean.getRouteRefs());
458                builder.addPropertyValue("restRefs", factoryBean.getRestRefs());
459                builder.addPropertyValue("globalOptions", factoryBean.getGlobalOptions());
460                builder.addPropertyValue("packageScan", factoryBean.getPackageScan());
461                builder.addPropertyValue("contextScan", factoryBean.getContextScan());
462                if (factoryBean.getPackages().length > 0) {
463                    builder.addPropertyValue("packages", factoryBean.getPackages());
464                }
465                builder.addPropertyValue("camelPropertyPlaceholder", factoryBean.getCamelPropertyPlaceholder());
466                builder.addPropertyValue("camelJMXAgent", factoryBean.getCamelJMXAgent());
467                builder.addPropertyValue("camelStreamCachingStrategy", factoryBean.getCamelStreamCachingStrategy());
468                builder.addPropertyValue("camelRouteController", factoryBean.getCamelRouteController());
469                builder.addPropertyValue("threadPoolProfiles", factoryBean.getThreadPoolProfiles());
470                builder.addPropertyValue("beansFactory", factoryBean.getBeansFactory());
471                builder.addPropertyValue("beans", factoryBean.getBeans());
472                builder.addPropertyValue("defaultServiceCallConfiguration", factoryBean.getDefaultServiceCallConfiguration());
473                builder.addPropertyValue("serviceCallConfigurations", factoryBean.getServiceCallConfigurations());
474                // add any depends-on
475                addDependsOn(factoryBean, builder);
476            }
477
478            NodeList list = element.getChildNodes();
479            int size = list.getLength();
480            for (int i = 0; i < size; i++) {
481                Node child = list.item(i);
482                if (child instanceof Element) {
483                    Element childElement = (Element) child;
484                    String localName = child.getLocalName();
485                    if (localName.equals("endpoint")) {
486                        registerEndpoint(childElement, parserContext, contextId);
487                    } else if (localName.equals("routeBuilder")) {
488                        addDependsOnToRouteBuilder(childElement, parserContext, contextId);
489                    } else {
490                        BeanDefinitionParser parser = parserMap.get(localName);
491                        if (parser != null) {
492                            BeanDefinition definition = parser.parse(childElement, parserContext);
493                            String id = childElement.getAttribute("id");
494                            if (ObjectHelper.isNotEmpty(id)) {
495                                parserContext.registerComponent(new BeanComponentDefinition(definition, id));
496                                // set the templates with the camel context
497                                if (localName.equals("template") || localName.equals("fluentTemplate")
498                                        || localName.equals("consumerTemplate")
499                                        || localName.equals("proxy") || localName.equals("export")) {
500                                    // set the camel context
501                                    definition.getPropertyValues().addPropertyValue("camelContext",
502                                            new RuntimeBeanReference(contextId));
503                                }
504                            }
505                        }
506                    }
507                }
508            }
509
510            // register templates if not already defined
511            registerTemplates(element, parserContext, contextId);
512
513            // lets inject the namespaces into any namespace aware POJOs
514            injectNamespaces(element, binder);
515
516            // inject bean post processor so we can support @Produce etc.
517            // no bean processor element so lets create it by our self
518            injectBeanPostProcessor(element, parserContext, contextId, builder, factoryBean);
519        }
520    }
521
522    protected void addDependsOn(CamelContextFactoryBean factoryBean, BeanDefinitionBuilder builder) {
523        String dependsOn = factoryBean.getDependsOn();
524        if (ObjectHelper.isNotEmpty(dependsOn)) {
525            // comma, whitespace and semi colon is valid separators in Spring depends-on
526            String[] depends = dependsOn.split(",|;|\\s");
527            if (depends == null) {
528                throw new IllegalArgumentException("Cannot separate depends-on, was: " + dependsOn);
529            } else {
530                for (String depend : depends) {
531                    depend = depend.trim();
532                    LOG.debug("Adding dependsOn {} to CamelContext({})", depend, factoryBean.getId());
533                    builder.addDependsOn(depend);
534                }
535            }
536        }
537    }
538
539    private void addDependsOnToRouteBuilder(Element childElement, ParserContext parserContext, String contextId) {
540        // setting the depends-on explicitly is required since Spring 3.0
541        String routeBuilderName = childElement.getAttribute("ref");
542        if (ObjectHelper.isNotEmpty(routeBuilderName)) {
543            // set depends-on to the context for a routeBuilder bean
544            try {
545                BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(routeBuilderName);
546                Method getDependsOn = definition.getClass().getMethod("getDependsOn", new Class[] {});
547                String[] dependsOn = (String[]) getDependsOn.invoke(definition);
548                if (dependsOn == null || dependsOn.length == 0) {
549                    dependsOn = new String[] { contextId };
550                } else {
551                    String[] temp = new String[dependsOn.length + 1];
552                    System.arraycopy(dependsOn, 0, temp, 0, dependsOn.length);
553                    temp[dependsOn.length] = contextId;
554                    dependsOn = temp;
555                }
556                Method method = definition.getClass().getMethod("setDependsOn", String[].class);
557                method.invoke(definition, (Object) dependsOn);
558            } catch (Exception e) {
559                // Do nothing here
560            }
561        }
562    }
563
564    protected void injectNamespaces(Element element, Binder<Node> binder) {
565        NodeList list = element.getChildNodes();
566        Namespaces namespaces = null;
567        int size = list.getLength();
568        for (int i = 0; i < size; i++) {
569            Node child = list.item(i);
570            if (child instanceof Element) {
571                Element childElement = (Element) child;
572                Object object = binder.getJAXBNode(child);
573                if (object instanceof NamespaceAware) {
574                    NamespaceAware namespaceAware = (NamespaceAware) object;
575                    if (namespaces == null) {
576                        namespaces = NamespacesHelper.namespaces(element);
577                    }
578                    namespaces.configure(namespaceAware);
579                }
580                injectNamespaces(childElement, binder);
581            }
582        }
583    }
584
585    protected void injectBeanPostProcessor(
586            Element element, ParserContext parserContext, String contextId, BeanDefinitionBuilder builder,
587            CamelContextFactoryBean factoryBean) {
588        Element childElement = element.getOwnerDocument().createElement("beanPostProcessor");
589        element.appendChild(childElement);
590
591        String beanPostProcessorId = contextId + ":beanPostProcessor";
592        childElement.setAttribute("id", beanPostProcessorId);
593        BeanDefinition definition = beanPostProcessorParser.parse(childElement, parserContext);
594        // only register to camel context id as a String. Then we can look it up later
595        // otherwise we get a circular reference in spring and it will not allow custom bean post processing
596        // see more at CAMEL-1663
597        definition.getPropertyValues().addPropertyValue("camelId", contextId);
598        if (factoryBean != null && factoryBean.getBeanPostProcessorEnabled() != null) {
599            // configure early whether bean post processor is enabled or not
600            definition.getPropertyValues().addPropertyValue("enabled", factoryBean.getBeanPostProcessorEnabled());
601        }
602        builder.addPropertyReference("beanPostProcessor", beanPostProcessorId);
603    }
604
605    /**
606     * Used for auto registering producer, fluent producer and consumer templates if not already defined in XML.
607     */
608    protected void registerTemplates(Element element, ParserContext parserContext, String contextId) {
609        boolean template = false;
610        boolean fluentTemplate = false;
611        boolean consumerTemplate = false;
612
613        NodeList list = element.getChildNodes();
614        int size = list.getLength();
615        for (int i = 0; i < size; i++) {
616            Node child = list.item(i);
617            if (child instanceof Element) {
618                Element childElement = (Element) child;
619                String localName = childElement.getLocalName();
620                if ("template".equals(localName)) {
621                    template = true;
622                } else if ("fluentTemplate".equals(localName)) {
623                    fluentTemplate = true;
624                } else if ("consumerTemplate".equals(localName)) {
625                    consumerTemplate = true;
626                }
627            }
628        }
629
630        if (!template) {
631            // either we have not used template before or we have auto registered it already and therefore we
632            // need it to allow to do it so it can remove the existing auto registered as there is now a clash id
633            // since we have multiple camel contexts
634            boolean existing = autoRegisterMap.get("template") != null;
635            boolean inUse = false;
636            try {
637                inUse = parserContext.getRegistry().isBeanNameInUse("template");
638            } catch (BeanCreationException e) {
639                // Spring Eclipse Tooling may throw an exception when you edit the Spring XML online in Eclipse
640                // when the isBeanNameInUse method is invoked, so ignore this and continue (CAMEL-2739)
641                LOG.debug("Error checking isBeanNameInUse(template). This exception will be ignored", e);
642            }
643            if (!inUse || existing) {
644                String id = "template";
645                // auto create a template
646                Element templateElement = element.getOwnerDocument().createElement("template");
647                templateElement.setAttribute("id", id);
648                BeanDefinitionParser parser = parserMap.get("template");
649                BeanDefinition definition = parser.parse(templateElement, parserContext);
650
651                // auto register it
652                autoRegisterBeanDefinition(id, definition, parserContext, contextId);
653            }
654        }
655
656        if (!fluentTemplate) {
657            // either we have not used fluentTemplate before or we have auto registered it already and therefore we
658            // need it to allow to do it so it can remove the existing auto registered as there is now a clash id
659            // since we have multiple camel contexts
660            boolean existing = autoRegisterMap.get("fluentTemplate") != null;
661            boolean inUse = false;
662            try {
663                inUse = parserContext.getRegistry().isBeanNameInUse("fluentTemplate");
664            } catch (BeanCreationException e) {
665                // Spring Eclipse Tooling may throw an exception when you edit the Spring XML online in Eclipse
666                // when the isBeanNameInUse method is invoked, so ignore this and continue (CAMEL-2739)
667                LOG.debug("Error checking isBeanNameInUse(fluentTemplate). This exception will be ignored", e);
668            }
669            if (!inUse || existing) {
670                String id = "fluentTemplate";
671                // auto create a fluentTemplate
672                Element templateElement = element.getOwnerDocument().createElement("fluentTemplate");
673                templateElement.setAttribute("id", id);
674                BeanDefinitionParser parser = parserMap.get("fluentTemplate");
675                BeanDefinition definition = parser.parse(templateElement, parserContext);
676
677                // auto register it
678                autoRegisterBeanDefinition(id, definition, parserContext, contextId);
679            }
680        }
681
682        if (!consumerTemplate) {
683            // either we have not used template before or we have auto registered it already and therefore we
684            // need it to allow to do it so it can remove the existing auto registered as there is now a clash id
685            // since we have multiple camel contexts
686            boolean existing = autoRegisterMap.get("consumerTemplate") != null;
687            boolean inUse = false;
688            try {
689                inUse = parserContext.getRegistry().isBeanNameInUse("consumerTemplate");
690            } catch (BeanCreationException e) {
691                // Spring Eclipse Tooling may throw an exception when you edit the Spring XML online in Eclipse
692                // when the isBeanNameInUse method is invoked, so ignore this and continue (CAMEL-2739)
693                LOG.debug("Error checking isBeanNameInUse(consumerTemplate). This exception will be ignored", e);
694            }
695            if (!inUse || existing) {
696                String id = "consumerTemplate";
697                // auto create a template
698                Element templateElement = element.getOwnerDocument().createElement("consumerTemplate");
699                templateElement.setAttribute("id", id);
700                BeanDefinitionParser parser = parserMap.get("consumerTemplate");
701                BeanDefinition definition = parser.parse(templateElement, parserContext);
702
703                // auto register it
704                autoRegisterBeanDefinition(id, definition, parserContext, contextId);
705            }
706        }
707
708    }
709
710    private void autoRegisterBeanDefinition(
711            String id, BeanDefinition definition, ParserContext parserContext, String contextId) {
712        // it is a bit cumbersome to work with the spring bean definition parser
713        // as we kinda need to eagerly register the bean definition on the parser context
714        // and then later we might find out that we should not have done that in case we have multiple camel contexts
715        // that would have a id clash by auto registering the same bean definition with the same id such as a producer template
716
717        // see if we have already auto registered this id
718        BeanDefinition existing = autoRegisterMap.get(id);
719        if (existing == null) {
720            // no then add it to the map and register it
721            autoRegisterMap.put(id, definition);
722            parserContext.registerComponent(new BeanComponentDefinition(definition, id));
723            if (LOG.isDebugEnabled()) {
724                LOG.debug("Registered default: {} with id: {} on camel context: {}", definition.getBeanClassName(), id,
725                        contextId);
726            }
727        } else {
728            // ups we have already registered it before with same id, but on another camel context
729            // this is not good so we need to remove all traces of this auto registering.
730            // end user must manually add the needed XML elements and provide unique ids access all camel context himself.
731            LOG.debug("Unregistered default: {} with id: {} as we have multiple camel contexts and they must use unique ids."
732                      + " You must define the definition in the XML file manually to avoid id clashes when using multiple camel contexts",
733                    definition.getBeanClassName(), id);
734
735            parserContext.getRegistry().removeBeanDefinition(id);
736        }
737    }
738
739    private void registerEndpoint(Element childElement, ParserContext parserContext, String contextId) {
740        String id = childElement.getAttribute("id");
741        // must have an id to be registered
742        if (ObjectHelper.isNotEmpty(id)) {
743            // skip underscore as they are internal naming and should not be registered
744            if (id.startsWith("_")) {
745                LOG.debug("Skip registering endpoint starting with underscore: {}", id);
746                return;
747            }
748            BeanDefinition definition = endpointParser.parse(childElement, parserContext);
749            definition.getPropertyValues().addPropertyValue("camelContext", new RuntimeBeanReference(contextId));
750            // Need to add this dependency of CamelContext for Spring 3.0
751            try {
752                Method method = definition.getClass().getMethod("setDependsOn", String[].class);
753                method.invoke(definition, (Object) new String[] { contextId });
754            } catch (Exception e) {
755                // Do nothing here
756            }
757            parserContext.registerComponent(new BeanComponentDefinition(definition, id));
758        }
759    }
760}