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