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.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.Node;
032import org.w3c.dom.NodeList;
033
034import org.apache.camel.builder.xml.Namespaces;
035import org.apache.camel.core.xml.CamelJMXAgentDefinition;
036import org.apache.camel.core.xml.CamelPropertyPlaceholderDefinition;
037import org.apache.camel.core.xml.CamelStreamCachingStrategyDefinition;
038import org.apache.camel.impl.DefaultCamelContextNameStrategy;
039import org.apache.camel.model.FromDefinition;
040import org.apache.camel.model.SendDefinition;
041import org.apache.camel.spi.CamelContextNameStrategy;
042import org.apache.camel.spi.NamespaceAware;
043import org.apache.camel.spring.CamelBeanPostProcessor;
044import org.apache.camel.spring.CamelConsumerTemplateFactoryBean;
045import org.apache.camel.spring.CamelContextFactoryBean;
046import org.apache.camel.spring.CamelEndpointFactoryBean;
047import org.apache.camel.spring.CamelProducerTemplateFactoryBean;
048import org.apache.camel.spring.CamelRedeliveryPolicyFactoryBean;
049import org.apache.camel.spring.CamelRouteContextFactoryBean;
050import org.apache.camel.spring.CamelThreadPoolFactoryBean;
051import org.apache.camel.spring.remoting.CamelProxyFactoryBean;
052import org.apache.camel.spring.remoting.CamelServiceExporter;
053import org.apache.camel.util.ObjectHelper;
054import org.apache.camel.util.spring.KeyStoreParametersFactoryBean;
055import org.apache.camel.util.spring.SSLContextParametersFactoryBean;
056import org.apache.camel.util.spring.SecureRandomParametersFactoryBean;
057import org.apache.camel.view.ModelFileGenerator;
058import org.slf4j.Logger;
059import org.slf4j.LoggerFactory;
060import org.springframework.beans.factory.BeanCreationException;
061import org.springframework.beans.factory.BeanDefinitionStoreException;
062import org.springframework.beans.factory.config.BeanDefinition;
063import org.springframework.beans.factory.config.RuntimeBeanReference;
064import org.springframework.beans.factory.parsing.BeanComponentDefinition;
065import org.springframework.beans.factory.support.BeanDefinitionBuilder;
066import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
067import org.springframework.beans.factory.xml.ParserContext;
068
069/**
070 * Camel namespace for the spring XML configuration file.
071 */
072public class CamelNamespaceHandler extends NamespaceHandlerSupport {
073    private static final String SPRING_NS = "http://camel.apache.org/schema/spring";
074    private static final Logger LOG = LoggerFactory.getLogger(CamelNamespaceHandler.class);
075    protected BeanDefinitionParser endpointParser = new BeanDefinitionParser(CamelEndpointFactoryBean.class, false);
076    protected BeanDefinitionParser beanPostProcessorParser = new BeanDefinitionParser(CamelBeanPostProcessor.class, false);
077    protected Set<String> parserElementNames = new HashSet<String>();
078    protected Map<String, BeanDefinitionParser> parserMap = new HashMap<String, BeanDefinitionParser>();
079    
080    private JAXBContext jaxbContext;
081    private Map<String, BeanDefinition> autoRegisterMap = new HashMap<String, BeanDefinition>();
082
083    public static void renameNamespaceRecursive(Node node) {
084        if (node.getNodeType() == Node.ELEMENT_NODE) {
085            Document doc = node.getOwnerDocument();
086            if (node.getNamespaceURI().startsWith(SPRING_NS + "/v")) {
087                doc.renameNode(node, SPRING_NS, node.getNodeName());
088            }
089        }
090        NodeList list = node.getChildNodes();
091        for (int i = 0; i < list.getLength(); ++i) {
092            renameNamespaceRecursive(list.item(i));
093        }
094    }
095
096    public ModelFileGenerator createModelFileGenerator() throws JAXBException {
097        return new ModelFileGenerator(getJaxbContext());
098    }
099
100    public void init() {
101        // register routeContext parser
102        registerParser("routeContext", new RouteContextDefinitionParser());
103        
104        addBeanDefinitionParser("keyStoreParameters", KeyStoreParametersFactoryBean.class, true, true);
105        addBeanDefinitionParser("secureRandomParameters", SecureRandomParametersFactoryBean.class, true, true);
106        registerBeanDefinitionParser("sslContextParameters", new SSLContextParametersFactoryBeanBeanDefinitionParser());
107
108        addBeanDefinitionParser("proxy", CamelProxyFactoryBean.class, true, false);
109        addBeanDefinitionParser("template", CamelProducerTemplateFactoryBean.class, true, false);
110        addBeanDefinitionParser("consumerTemplate", CamelConsumerTemplateFactoryBean.class, true, false);
111        addBeanDefinitionParser("export", CamelServiceExporter.class, true, false);
112        addBeanDefinitionParser("endpoint", CamelEndpointFactoryBean.class, true, false);
113        addBeanDefinitionParser("threadPool", CamelThreadPoolFactoryBean.class, true, true);
114        addBeanDefinitionParser("redeliveryPolicyProfile", CamelRedeliveryPolicyFactoryBean.class, true, true);
115
116        // jmx agent, stream caching, and property placeholder cannot be used outside of the camel context
117        addBeanDefinitionParser("jmxAgent", CamelJMXAgentDefinition.class, false, false);
118        addBeanDefinitionParser("streamCaching", CamelStreamCachingStrategyDefinition.class, false, false);
119        addBeanDefinitionParser("propertyPlaceholder", CamelPropertyPlaceholderDefinition.class, false, false);
120
121        // errorhandler could be the sub element of camelContext or defined outside camelContext
122        BeanDefinitionParser errorHandlerParser = new ErrorHandlerDefinitionParser();
123        registerParser("errorHandler", errorHandlerParser);
124        parserMap.put("errorHandler", errorHandlerParser);
125
126        // camel context
127        boolean osgi = false;
128        Class<?> cl = CamelContextFactoryBean.class;
129        // These code will try to detected if we are in the OSGi environment.
130        // If so, camel will use the OSGi version of CamelContextFactoryBean to create the CamelContext.
131        try {
132            // Try to load the BundleActivator first
133            Class.forName("org.osgi.framework.BundleActivator");
134            Class<?> c = Class.forName("org.apache.camel.osgi.Activator");
135            Method mth = c.getDeclaredMethod("getBundle");
136            Object bundle = mth.invoke(null);
137            if (bundle != null) {
138                cl = Class.forName("org.apache.camel.osgi.CamelContextFactoryBean");
139                osgi = true;
140            }
141        } catch (Throwable t) {
142            // not running with camel-core-osgi so we fallback to the regular factory bean
143            LOG.trace("Cannot find class so assuming not running in OSGi container: " + t.getMessage());
144        }
145        if (osgi) {
146            LOG.info("OSGi environment detected.");
147        } 
148        LOG.debug("Using {} as CamelContextBeanDefinitionParser", cl.getCanonicalName());
149        registerParser("camelContext", new CamelContextBeanDefinitionParser(cl));
150    }
151
152    protected void addBeanDefinitionParser(String elementName, Class<?> type, boolean register, boolean assignId) {
153        BeanDefinitionParser parser = new BeanDefinitionParser(type, assignId);
154        if (register) {
155            registerParser(elementName, parser);
156        }
157        parserMap.put(elementName, parser);
158    }
159
160    protected void registerParser(String name, org.springframework.beans.factory.xml.BeanDefinitionParser parser) {
161        parserElementNames.add(name);
162        registerBeanDefinitionParser(name, parser);
163    }
164
165    protected Object parseUsingJaxb(Element element, ParserContext parserContext, Binder<Node> binder) {
166        try {
167            return binder.unmarshal(element);
168        } catch (JAXBException e) {
169            throw new BeanDefinitionStoreException("Failed to parse JAXB element", e);
170        }
171    }
172
173    public JAXBContext getJaxbContext() throws JAXBException {
174        if (jaxbContext == null) {
175            jaxbContext = createJaxbContext();
176        }
177        return jaxbContext;
178    }
179
180    protected JAXBContext createJaxbContext() throws JAXBException {
181        StringBuilder packages = new StringBuilder();
182        for (Class<?> cl : getJaxbPackages()) {
183            if (packages.length() > 0) {
184                packages.append(":");
185            }
186            packages.append(cl.getName().substring(0, cl.getName().lastIndexOf('.')));
187        }
188        return JAXBContext.newInstance(packages.toString(), getClass().getClassLoader());
189    }
190
191    protected Set<Class<?>> getJaxbPackages() {
192        Set<Class<?>> classes = new HashSet<Class<?>>();
193        classes.add(org.apache.camel.spring.CamelContextFactoryBean.class);
194        classes.add(CamelJMXAgentDefinition.class);
195        classes.add(org.apache.camel.ExchangePattern.class);
196        classes.add(org.apache.camel.model.RouteDefinition.class);
197        classes.add(org.apache.camel.model.config.StreamResequencerConfig.class);
198        classes.add(org.apache.camel.model.dataformat.DataFormatsDefinition.class);
199        classes.add(org.apache.camel.model.language.ExpressionDefinition.class);
200        classes.add(org.apache.camel.model.loadbalancer.RoundRobinLoadBalancerDefinition.class);
201        classes.add(org.apache.camel.util.spring.SSLContextParametersFactoryBean.class);
202        return classes;
203    }
204    
205    protected class SSLContextParametersFactoryBeanBeanDefinitionParser extends BeanDefinitionParser {
206
207        public SSLContextParametersFactoryBeanBeanDefinitionParser() {
208            super(SSLContextParametersFactoryBean.class, true);
209        }
210
211        @Override
212        protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
213            super.doParse(element, builder);
214            
215            // Note: prefer to use doParse from parent and postProcess; however, parseUsingJaxb requires 
216            // parserContext for no apparent reason.
217            Binder<Node> binder;
218            try {
219                binder = getJaxbContext().createBinder();
220            } catch (JAXBException e) {
221                throw new BeanDefinitionStoreException("Failed to create the JAXB binder", e);
222            }
223            
224            Object value = parseUsingJaxb(element, parserContext, binder);
225            
226            if (value instanceof SSLContextParametersFactoryBean) {
227                SSLContextParametersFactoryBean bean = (SSLContextParametersFactoryBean)value;
228                
229                builder.addPropertyValue("cipherSuites", bean.getCipherSuites());
230                builder.addPropertyValue("cipherSuitesFilter", bean.getCipherSuitesFilter());
231                builder.addPropertyValue("secureSocketProtocols", bean.getSecureSocketProtocols());
232                builder.addPropertyValue("secureSocketProtocolsFilter", bean.getSecureSocketProtocolsFilter());
233                builder.addPropertyValue("keyManagers", bean.getKeyManagers());
234                builder.addPropertyValue("trustManagers", bean.getTrustManagers());
235                builder.addPropertyValue("secureRandom", bean.getSecureRandom());
236                
237                builder.addPropertyValue("clientParameters", bean.getClientParameters());
238                builder.addPropertyValue("serverParameters", bean.getServerParameters());
239            } else {
240                throw new BeanDefinitionStoreException("Parsed type is not of the expected type. Expected "
241                                                       + SSLContextParametersFactoryBean.class.getName() + " but found "
242                                                       + value.getClass().getName());
243            }
244        }
245    }
246
247    protected class RouteContextDefinitionParser extends BeanDefinitionParser {
248
249        public RouteContextDefinitionParser() {
250            super(CamelRouteContextFactoryBean.class, false);
251        }
252
253        @Override
254        protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
255            renameNamespaceRecursive(element);
256            super.doParse(element, parserContext, builder);
257
258            // now lets parse the routes with JAXB
259            Binder<Node> binder;
260            try {
261                binder = getJaxbContext().createBinder();
262            } catch (JAXBException e) {
263                throw new BeanDefinitionStoreException("Failed to create the JAXB binder", e);
264            }
265            Object value = parseUsingJaxb(element, parserContext, binder);
266
267            if (value instanceof CamelRouteContextFactoryBean) {
268                CamelRouteContextFactoryBean factoryBean = (CamelRouteContextFactoryBean) value;
269                builder.addPropertyValue("routes", factoryBean.getRoutes());
270            }
271
272            // lets inject the namespaces into any namespace aware POJOs
273            injectNamespaces(element, binder);
274        }
275    }
276
277    protected class CamelContextBeanDefinitionParser extends BeanDefinitionParser {
278
279        public CamelContextBeanDefinitionParser(Class<?> type) {
280            super(type, false);
281        }
282
283        @Override
284        protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
285            renameNamespaceRecursive(element);
286            super.doParse(element, parserContext, builder);
287
288            String contextId = element.getAttribute("id");
289            boolean implicitId = false;
290
291            // lets avoid folks having to explicitly give an ID to a camel context
292            if (ObjectHelper.isEmpty(contextId)) {
293                // if no explicit id was set then use a default auto generated name
294                CamelContextNameStrategy strategy = new DefaultCamelContextNameStrategy();
295                contextId = strategy.getName();
296                element.setAttributeNS(null, "id", contextId);
297                implicitId = true;
298            }
299
300            // now lets parse the routes with JAXB
301            Binder<Node> binder;
302            try {
303                binder = getJaxbContext().createBinder();
304            } catch (JAXBException e) {
305                throw new BeanDefinitionStoreException("Failed to create the JAXB binder", e);
306            }
307            Object value = parseUsingJaxb(element, parserContext, binder);
308
309            if (value instanceof CamelContextFactoryBean) {
310                // set the property value with the JAXB parsed value
311                CamelContextFactoryBean factoryBean = (CamelContextFactoryBean) value;
312                builder.addPropertyValue("id", contextId);
313                builder.addPropertyValue("implicitId", implicitId);
314                builder.addPropertyValue("routes", factoryBean.getRoutes());
315                builder.addPropertyValue("intercepts", factoryBean.getIntercepts());
316                builder.addPropertyValue("interceptFroms", factoryBean.getInterceptFroms());
317                builder.addPropertyValue("interceptSendToEndpoints", factoryBean.getInterceptSendToEndpoints());
318                builder.addPropertyValue("dataFormats", factoryBean.getDataFormats());
319                builder.addPropertyValue("onCompletions", factoryBean.getOnCompletions());
320                builder.addPropertyValue("onExceptions", factoryBean.getOnExceptions());
321                builder.addPropertyValue("builderRefs", factoryBean.getBuilderRefs());
322                builder.addPropertyValue("routeRefs", factoryBean.getRouteRefs());
323                builder.addPropertyValue("properties", factoryBean.getProperties());
324                builder.addPropertyValue("packageScan", factoryBean.getPackageScan());
325                builder.addPropertyValue("contextScan", factoryBean.getContextScan());
326                if (factoryBean.getPackages().length > 0) {
327                    builder.addPropertyValue("packages", factoryBean.getPackages());
328                }
329                builder.addPropertyValue("camelPropertyPlaceholder", factoryBean.getCamelPropertyPlaceholder());
330                builder.addPropertyValue("camelJMXAgent", factoryBean.getCamelJMXAgent());
331                builder.addPropertyValue("camelStreamCachingStrategy", factoryBean.getCamelStreamCachingStrategy());
332                builder.addPropertyValue("threadPoolProfiles", factoryBean.getThreadPoolProfiles());
333                // add any depends-on
334                addDependsOn(factoryBean, builder);
335            }
336
337            NodeList list = element.getChildNodes();
338            int size = list.getLength();
339            for (int i = 0; i < size; i++) {
340                Node child = list.item(i);
341                if (child instanceof Element) {
342                    Element childElement = (Element) child;
343                    String localName = child.getLocalName();
344                    if (localName.equals("endpoint")) {
345                        registerEndpoint(childElement, parserContext, contextId);
346                    } else if (localName.equals("routeBuilder")) {
347                        addDependsOnToRouteBuilder(childElement, parserContext, contextId);
348                    } else {
349                        BeanDefinitionParser parser = parserMap.get(localName);
350                        if (parser != null) {
351                            BeanDefinition definition = parser.parse(childElement, parserContext);
352                            String id = childElement.getAttribute("id");
353                            if (ObjectHelper.isNotEmpty(id)) {
354                                parserContext.registerComponent(new BeanComponentDefinition(definition, id));
355                                // set the templates with the camel context
356                                if (localName.equals("template") || localName.equals("consumerTemplate")
357                                        || localName.equals("proxy") || localName.equals("export")) {
358                                    // set the camel context
359                                    definition.getPropertyValues().addPropertyValue("camelContext", new RuntimeBeanReference(contextId));
360                                }
361                            }
362                        }
363                    }
364                }
365            }
366
367            // register as endpoint defined indirectly in the routes by from/to types having id explicit set
368            registerEndpointsWithIdsDefinedInFromOrToTypes(element, parserContext, contextId, binder);
369
370            // register templates if not already defined
371            registerTemplates(element, parserContext, contextId);
372
373            // lets inject the namespaces into any namespace aware POJOs
374            injectNamespaces(element, binder);
375
376            // inject bean post processor so we can support @Produce etc.
377            // no bean processor element so lets create it by our self
378            injectBeanPostProcessor(element, parserContext, contextId, builder);
379        }
380    }
381
382    protected void addDependsOn(CamelContextFactoryBean factoryBean, BeanDefinitionBuilder builder) {
383        String dependsOn = factoryBean.getDependsOn();
384        if (ObjectHelper.isNotEmpty(dependsOn)) {
385            // comma, whitespace and semi colon is valid separators in Spring depends-on
386            String[] depends = dependsOn.split(",|;|\\s");
387            if (depends == null) {
388                throw new IllegalArgumentException("Cannot separate depends-on, was: " + dependsOn);
389            } else {
390                for (String depend : depends) {
391                    depend = depend.trim();
392                    LOG.debug("Adding dependsOn {} to CamelContext({})", depend, factoryBean.getId());
393                    builder.addDependsOn(depend);
394                }
395            }
396        }
397    }
398
399    private void addDependsOnToRouteBuilder(Element childElement, ParserContext parserContext, String contextId) {
400        // setting the depends-on explicitly is required since Spring 3.0
401        String routeBuilderName = childElement.getAttribute("ref");
402        if (ObjectHelper.isNotEmpty(routeBuilderName)) {
403            // set depends-on to the context for a routeBuilder bean
404            try {
405                BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(routeBuilderName);
406                Method getDependsOn = definition.getClass().getMethod("getDependsOn", new Class[]{});
407                String[] dependsOn = (String[])getDependsOn.invoke(definition);
408                if (dependsOn == null || dependsOn.length == 0) {
409                    dependsOn = new String[]{contextId};
410                } else {
411                    String[] temp = new String[dependsOn.length + 1];
412                    System.arraycopy(dependsOn, 0, temp, 0, dependsOn.length);
413                    temp[dependsOn.length] = contextId;
414                    dependsOn = temp;
415                }
416                Method method = definition.getClass().getMethod("setDependsOn", String[].class);
417                method.invoke(definition, (Object)dependsOn);
418            } catch (Exception e) {
419                // Do nothing here
420            }
421        }
422    }
423
424    protected void injectNamespaces(Element element, Binder<Node> binder) {
425        NodeList list = element.getChildNodes();
426        Namespaces namespaces = null;
427        int size = list.getLength();
428        for (int i = 0; i < size; i++) {
429            Node child = list.item(i);
430            if (child instanceof Element) {
431                Element childElement = (Element) child;
432                Object object = binder.getJAXBNode(child);
433                if (object instanceof NamespaceAware) {
434                    NamespaceAware namespaceAware = (NamespaceAware) object;
435                    if (namespaces == null) {
436                        namespaces = new Namespaces(element);
437                    }
438                    namespaces.configure(namespaceAware);
439                }
440                injectNamespaces(childElement, binder);
441            }
442        }
443    }
444
445    protected void injectBeanPostProcessor(Element element, ParserContext parserContext, String contextId, BeanDefinitionBuilder builder) {
446        Element childElement = element.getOwnerDocument().createElement("beanPostProcessor");
447        element.appendChild(childElement);
448
449        String beanPostProcessorId = contextId + ":beanPostProcessor";
450        childElement.setAttribute("id", beanPostProcessorId);
451        BeanDefinition definition = beanPostProcessorParser.parse(childElement, parserContext);
452        // only register to camel context id as a String. Then we can look it up later
453        // otherwise we get a circular reference in spring and it will not allow custom bean post processing
454        // see more at CAMEL-1663
455        definition.getPropertyValues().addPropertyValue("camelId", contextId);
456        builder.addPropertyReference("beanPostProcessor", beanPostProcessorId);
457    }
458
459    /**
460     * Used for auto registering endpoints from the <tt>from</tt> or <tt>to</tt> DSL if they have an id attribute set
461     */
462    protected void registerEndpointsWithIdsDefinedInFromOrToTypes(Element element, ParserContext parserContext, String contextId, Binder<Node> binder) {
463        NodeList list = element.getChildNodes();
464        int size = list.getLength();
465        for (int i = 0; i < size; i++) {
466            Node child = list.item(i);
467            if (child instanceof Element) {
468                Element childElement = (Element) child;
469                Object object = binder.getJAXBNode(child);
470                // we only want from/to types to be registered as endpoints
471                if (object instanceof FromDefinition || object instanceof SendDefinition) {
472                    registerEndpoint(childElement, parserContext, contextId);
473                }
474                // recursive
475                registerEndpointsWithIdsDefinedInFromOrToTypes(childElement, parserContext, contextId, binder);
476            }
477        }
478    }
479
480    /**
481     * Used for auto registering producer and consumer templates if not already defined in XML.
482     */
483    protected void registerTemplates(Element element, ParserContext parserContext, String contextId) {
484        boolean template = false;
485        boolean consumerTemplate = false;
486
487        NodeList list = element.getChildNodes();
488        int size = list.getLength();
489        for (int i = 0; i < size; i++) {
490            Node child = list.item(i);
491            if (child instanceof Element) {
492                Element childElement = (Element) child;
493                String localName = childElement.getLocalName();
494                if ("template".equals(localName)) {
495                    template = true;
496                } else if ("consumerTemplate".equals(localName)) {
497                    consumerTemplate = true;
498                }
499            }
500        }
501
502        if (!template) {
503            // either we have not used template before or we have auto registered it already and therefore we
504            // need it to allow to do it so it can remove the existing auto registered as there is now a clash id
505            // since we have multiple camel contexts
506            boolean existing = autoRegisterMap.get("template") != null;
507            boolean inUse = false;
508            try {
509                inUse = parserContext.getRegistry().isBeanNameInUse("template");
510            } catch (BeanCreationException e) {
511                // Spring Eclipse Tooling may throw an exception when you edit the Spring XML online in Eclipse
512                // when the isBeanNameInUse method is invoked, so ignore this and continue (CAMEL-2739)
513                LOG.debug("Error checking isBeanNameInUse(template). This exception will be ignored", e);
514            }
515            if (!inUse || existing) {
516                String id = "template";
517                // auto create a template
518                Element templateElement = element.getOwnerDocument().createElement("template");
519                templateElement.setAttribute("id", id);
520                BeanDefinitionParser parser = parserMap.get("template");
521                BeanDefinition definition = parser.parse(templateElement, parserContext);
522
523                // auto register it
524                autoRegisterBeanDefinition(id, definition, parserContext, contextId);
525            }
526        }
527
528        if (!consumerTemplate) {
529            // either we have not used template before or we have auto registered it already and therefore we
530            // need it to allow to do it so it can remove the existing auto registered as there is now a clash id
531            // since we have multiple camel contexts
532            boolean existing = autoRegisterMap.get("consumerTemplate") != null;
533            boolean inUse = false;
534            try {
535                inUse = parserContext.getRegistry().isBeanNameInUse("consumerTemplate");
536            } catch (BeanCreationException e) {
537                // Spring Eclipse Tooling may throw an exception when you edit the Spring XML online in Eclipse
538                // when the isBeanNameInUse method is invoked, so ignore this and continue (CAMEL-2739)
539                LOG.debug("Error checking isBeanNameInUse(consumerTemplate). This exception will be ignored", e);
540            }
541            if (!inUse || existing) {
542                String id = "consumerTemplate";
543                // auto create a template
544                Element templateElement = element.getOwnerDocument().createElement("consumerTemplate");
545                templateElement.setAttribute("id", id);
546                BeanDefinitionParser parser = parserMap.get("consumerTemplate");
547                BeanDefinition definition = parser.parse(templateElement, parserContext);
548
549                // auto register it
550                autoRegisterBeanDefinition(id, definition, parserContext, contextId);
551            }
552        }
553
554    }
555
556    private void autoRegisterBeanDefinition(String id, BeanDefinition definition, ParserContext parserContext, String contextId) {
557        // it is a bit cumbersome to work with the spring bean definition parser
558        // as we kinda need to eagerly register the bean definition on the parser context
559        // and then later we might find out that we should not have done that in case we have multiple camel contexts
560        // that would have a id clash by auto registering the same bean definition with the same id such as a producer template
561
562        // see if we have already auto registered this id
563        BeanDefinition existing = autoRegisterMap.get(id);
564        if (existing == null) {
565            // no then add it to the map and register it
566            autoRegisterMap.put(id, definition);
567            parserContext.registerComponent(new BeanComponentDefinition(definition, id));
568            if (LOG.isDebugEnabled()) {
569                LOG.debug("Registered default: {} with id: {} on camel context: {}", new Object[]{definition.getBeanClassName(), id, contextId});
570            }
571        } else {
572            // ups we have already registered it before with same id, but on another camel context
573            // this is not good so we need to remove all traces of this auto registering.
574            // end user must manually add the needed XML elements and provide unique ids access all camel context himself.
575            LOG.debug("Unregistered default: {} with id: {} as we have multiple camel contexts and they must use unique ids."
576                    + " You must define the definition in the XML file manually to avoid id clashes when using multiple camel contexts",
577                    definition.getBeanClassName(), id);
578
579            parserContext.getRegistry().removeBeanDefinition(id);
580        }
581    }
582
583    private void registerEndpoint(Element childElement, ParserContext parserContext, String contextId) {
584        String id = childElement.getAttribute("id");
585        // must have an id to be registered
586        if (ObjectHelper.isNotEmpty(id)) {
587            BeanDefinition definition = endpointParser.parse(childElement, parserContext);
588            definition.getPropertyValues().addPropertyValue("camelContext", new RuntimeBeanReference(contextId));
589            // Need to add this dependency of CamelContext for Spring 3.0
590            try {
591                Method method = definition.getClass().getMethod("setDependsOn", String[].class);
592                method.invoke(definition, (Object) new String[]{contextId});
593            } catch (Exception e) {
594                // Do nothing here
595            }
596            parserContext.registerBeanComponent(new BeanComponentDefinition(definition, id));
597        }
598    }
599
600}