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