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("templatedRoutes", factoryBean.getTemplatedRoutes()); 429 builder.addPropertyValue("routes", factoryBean.getRoutes()); 430 builder.addPropertyValue("intercepts", factoryBean.getIntercepts()); 431 builder.addPropertyValue("interceptFroms", factoryBean.getInterceptFroms()); 432 builder.addPropertyValue("interceptSendToEndpoints", factoryBean.getInterceptSendToEndpoints()); 433 builder.addPropertyValue("dataFormats", factoryBean.getDataFormats()); 434 builder.addPropertyValue("transformers", factoryBean.getTransformers()); 435 builder.addPropertyValue("validators", factoryBean.getValidators()); 436 builder.addPropertyValue("onCompletions", factoryBean.getOnCompletions()); 437 builder.addPropertyValue("onExceptions", factoryBean.getOnExceptions()); 438 builder.addPropertyValue("routeConfigurationRefs", factoryBean.getRouteConfigurationRefs()); 439 builder.addPropertyValue("routeTemplateRefs", factoryBean.getRouteTemplateRefs()); 440 builder.addPropertyValue("builderRefs", factoryBean.getBuilderRefs()); 441 builder.addPropertyValue("routeRefs", factoryBean.getRouteRefs()); 442 builder.addPropertyValue("restRefs", factoryBean.getRestRefs()); 443 builder.addPropertyValue("globalOptions", factoryBean.getGlobalOptions()); 444 builder.addPropertyValue("packageScan", factoryBean.getPackageScan()); 445 builder.addPropertyValue("contextScan", factoryBean.getContextScan()); 446 if (factoryBean.getPackages().length > 0) { 447 builder.addPropertyValue("packages", factoryBean.getPackages()); 448 } 449 builder.addPropertyValue("camelPropertyPlaceholder", factoryBean.getCamelPropertyPlaceholder()); 450 builder.addPropertyValue("camelJMXAgent", factoryBean.getCamelJMXAgent()); 451 builder.addPropertyValue("camelStreamCachingStrategy", factoryBean.getCamelStreamCachingStrategy()); 452 builder.addPropertyValue("camelRouteController", factoryBean.getCamelRouteController()); 453 builder.addPropertyValue("threadPoolProfiles", factoryBean.getThreadPoolProfiles()); 454 builder.addPropertyValue("beansFactory", factoryBean.getBeansFactory()); 455 builder.addPropertyValue("beans", factoryBean.getBeans()); 456 builder.addPropertyValue("defaultServiceCallConfiguration", factoryBean.getDefaultServiceCallConfiguration()); 457 builder.addPropertyValue("serviceCallConfigurations", factoryBean.getServiceCallConfigurations()); 458 builder.addPropertyValue("defaultHystrixConfiguration", factoryBean.getDefaultHystrixConfiguration()); 459 builder.addPropertyValue("hystrixConfigurations", factoryBean.getHystrixConfigurations()); 460 // add any depends-on 461 addDependsOn(factoryBean, builder); 462 } 463 464 NodeList list = element.getChildNodes(); 465 int size = list.getLength(); 466 for (int i = 0; i < size; i++) { 467 Node child = list.item(i); 468 if (child instanceof Element) { 469 Element childElement = (Element) child; 470 String localName = child.getLocalName(); 471 if (localName.equals("endpoint")) { 472 registerEndpoint(childElement, parserContext, contextId); 473 } else if (localName.equals("routeBuilder")) { 474 addDependsOnToRouteBuilder(childElement, parserContext, contextId); 475 } else { 476 BeanDefinitionParser parser = parserMap.get(localName); 477 if (parser != null) { 478 BeanDefinition definition = parser.parse(childElement, parserContext); 479 String id = childElement.getAttribute("id"); 480 if (ObjectHelper.isNotEmpty(id)) { 481 parserContext.registerComponent(new BeanComponentDefinition(definition, id)); 482 // set the templates with the camel context 483 if (localName.equals("template") || localName.equals("fluentTemplate") 484 || localName.equals("consumerTemplate") 485 || localName.equals("proxy") || localName.equals("export")) { 486 // set the camel context 487 definition.getPropertyValues().addPropertyValue("camelContext", 488 new RuntimeBeanReference(contextId)); 489 } 490 } 491 } 492 } 493 } 494 } 495 496 // register templates if not already defined 497 registerTemplates(element, parserContext, contextId); 498 499 // lets inject the namespaces into any namespace aware POJOs 500 injectNamespaces(element, binder); 501 502 // inject bean post processor so we can support @Produce etc. 503 // no bean processor element so lets create it by our self 504 injectBeanPostProcessor(element, parserContext, contextId, builder, factoryBean); 505 } 506 } 507 508 protected void addDependsOn(CamelContextFactoryBean factoryBean, BeanDefinitionBuilder builder) { 509 String dependsOn = factoryBean.getDependsOn(); 510 if (ObjectHelper.isNotEmpty(dependsOn)) { 511 // comma, whitespace and semi colon is valid separators in Spring depends-on 512 String[] depends = dependsOn.split(",|;|\\s"); 513 if (depends == null) { 514 throw new IllegalArgumentException("Cannot separate depends-on, was: " + dependsOn); 515 } else { 516 for (String depend : depends) { 517 depend = depend.trim(); 518 LOG.debug("Adding dependsOn {} to CamelContext({})", depend, factoryBean.getId()); 519 builder.addDependsOn(depend); 520 } 521 } 522 } 523 } 524 525 private void addDependsOnToRouteBuilder(Element childElement, ParserContext parserContext, String contextId) { 526 // setting the depends-on explicitly is required since Spring 3.0 527 String routeBuilderName = childElement.getAttribute("ref"); 528 if (ObjectHelper.isNotEmpty(routeBuilderName)) { 529 // set depends-on to the context for a routeBuilder bean 530 try { 531 BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(routeBuilderName); 532 Method getDependsOn = definition.getClass().getMethod("getDependsOn", new Class[] {}); 533 String[] dependsOn = (String[]) getDependsOn.invoke(definition); 534 if (dependsOn == null || dependsOn.length == 0) { 535 dependsOn = new String[] { contextId }; 536 } else { 537 String[] temp = new String[dependsOn.length + 1]; 538 System.arraycopy(dependsOn, 0, temp, 0, dependsOn.length); 539 temp[dependsOn.length] = contextId; 540 dependsOn = temp; 541 } 542 Method method = definition.getClass().getMethod("setDependsOn", String[].class); 543 method.invoke(definition, (Object) dependsOn); 544 } catch (Exception e) { 545 // Do nothing here 546 } 547 } 548 } 549 550 protected void injectNamespaces(Element element, Binder<Node> binder) { 551 NodeList list = element.getChildNodes(); 552 Namespaces namespaces = null; 553 int size = list.getLength(); 554 for (int i = 0; i < size; i++) { 555 Node child = list.item(i); 556 if (child instanceof Element) { 557 Element childElement = (Element) child; 558 Object object = binder.getJAXBNode(child); 559 if (object instanceof NamespaceAware) { 560 NamespaceAware namespaceAware = (NamespaceAware) object; 561 if (namespaces == null) { 562 namespaces = NamespacesHelper.namespaces(element); 563 } 564 namespaces.configure(namespaceAware); 565 } 566 injectNamespaces(childElement, binder); 567 } 568 } 569 } 570 571 protected void injectBeanPostProcessor( 572 Element element, ParserContext parserContext, String contextId, BeanDefinitionBuilder builder, 573 CamelContextFactoryBean factoryBean) { 574 Element childElement = element.getOwnerDocument().createElement("beanPostProcessor"); 575 element.appendChild(childElement); 576 577 String beanPostProcessorId = contextId + ":beanPostProcessor"; 578 childElement.setAttribute("id", beanPostProcessorId); 579 BeanDefinition definition = beanPostProcessorParser.parse(childElement, parserContext); 580 // only register to camel context id as a String. Then we can look it up later 581 // otherwise we get a circular reference in spring and it will not allow custom bean post processing 582 // see more at CAMEL-1663 583 definition.getPropertyValues().addPropertyValue("camelId", contextId); 584 if (factoryBean != null && factoryBean.getBeanPostProcessorEnabled() != null) { 585 // configure early whether bean post processor is enabled or not 586 definition.getPropertyValues().addPropertyValue("enabled", factoryBean.getBeanPostProcessorEnabled()); 587 } 588 builder.addPropertyReference("beanPostProcessor", beanPostProcessorId); 589 } 590 591 /** 592 * Used for auto registering producer, fluent producer and consumer templates if not already defined in XML. 593 */ 594 protected void registerTemplates(Element element, ParserContext parserContext, String contextId) { 595 boolean template = false; 596 boolean fluentTemplate = false; 597 boolean consumerTemplate = false; 598 599 NodeList list = element.getChildNodes(); 600 int size = list.getLength(); 601 for (int i = 0; i < size; i++) { 602 Node child = list.item(i); 603 if (child instanceof Element) { 604 Element childElement = (Element) child; 605 String localName = childElement.getLocalName(); 606 if ("template".equals(localName)) { 607 template = true; 608 } else if ("fluentTemplate".equals(localName)) { 609 fluentTemplate = true; 610 } else if ("consumerTemplate".equals(localName)) { 611 consumerTemplate = true; 612 } 613 } 614 } 615 616 if (!template) { 617 // either we have not used template before or we have auto registered it already and therefore we 618 // need it to allow to do it so it can remove the existing auto registered as there is now a clash id 619 // since we have multiple camel contexts 620 boolean existing = autoRegisterMap.get("template") != null; 621 boolean inUse = false; 622 try { 623 inUse = parserContext.getRegistry().isBeanNameInUse("template"); 624 } catch (BeanCreationException e) { 625 // Spring Eclipse Tooling may throw an exception when you edit the Spring XML online in Eclipse 626 // when the isBeanNameInUse method is invoked, so ignore this and continue (CAMEL-2739) 627 LOG.debug("Error checking isBeanNameInUse(template). This exception will be ignored", e); 628 } 629 if (!inUse || existing) { 630 String id = "template"; 631 // auto create a template 632 Element templateElement = element.getOwnerDocument().createElement("template"); 633 templateElement.setAttribute("id", id); 634 BeanDefinitionParser parser = parserMap.get("template"); 635 BeanDefinition definition = parser.parse(templateElement, parserContext); 636 637 // auto register it 638 autoRegisterBeanDefinition(id, definition, parserContext, contextId); 639 } 640 } 641 642 if (!fluentTemplate) { 643 // either we have not used fluentTemplate before or we have auto registered it already and therefore we 644 // need it to allow to do it so it can remove the existing auto registered as there is now a clash id 645 // since we have multiple camel contexts 646 boolean existing = autoRegisterMap.get("fluentTemplate") != null; 647 boolean inUse = false; 648 try { 649 inUse = parserContext.getRegistry().isBeanNameInUse("fluentTemplate"); 650 } catch (BeanCreationException e) { 651 // Spring Eclipse Tooling may throw an exception when you edit the Spring XML online in Eclipse 652 // when the isBeanNameInUse method is invoked, so ignore this and continue (CAMEL-2739) 653 LOG.debug("Error checking isBeanNameInUse(fluentTemplate). This exception will be ignored", e); 654 } 655 if (!inUse || existing) { 656 String id = "fluentTemplate"; 657 // auto create a fluentTemplate 658 Element templateElement = element.getOwnerDocument().createElement("fluentTemplate"); 659 templateElement.setAttribute("id", id); 660 BeanDefinitionParser parser = parserMap.get("fluentTemplate"); 661 BeanDefinition definition = parser.parse(templateElement, parserContext); 662 663 // auto register it 664 autoRegisterBeanDefinition(id, definition, parserContext, contextId); 665 } 666 } 667 668 if (!consumerTemplate) { 669 // either we have not used template before or we have auto registered it already and therefore we 670 // need it to allow to do it so it can remove the existing auto registered as there is now a clash id 671 // since we have multiple camel contexts 672 boolean existing = autoRegisterMap.get("consumerTemplate") != null; 673 boolean inUse = false; 674 try { 675 inUse = parserContext.getRegistry().isBeanNameInUse("consumerTemplate"); 676 } catch (BeanCreationException e) { 677 // Spring Eclipse Tooling may throw an exception when you edit the Spring XML online in Eclipse 678 // when the isBeanNameInUse method is invoked, so ignore this and continue (CAMEL-2739) 679 LOG.debug("Error checking isBeanNameInUse(consumerTemplate). This exception will be ignored", e); 680 } 681 if (!inUse || existing) { 682 String id = "consumerTemplate"; 683 // auto create a template 684 Element templateElement = element.getOwnerDocument().createElement("consumerTemplate"); 685 templateElement.setAttribute("id", id); 686 BeanDefinitionParser parser = parserMap.get("consumerTemplate"); 687 BeanDefinition definition = parser.parse(templateElement, parserContext); 688 689 // auto register it 690 autoRegisterBeanDefinition(id, definition, parserContext, contextId); 691 } 692 } 693 694 } 695 696 private void autoRegisterBeanDefinition( 697 String id, BeanDefinition definition, ParserContext parserContext, String contextId) { 698 // it is a bit cumbersome to work with the spring bean definition parser 699 // as we kinda need to eagerly register the bean definition on the parser context 700 // and then later we might find out that we should not have done that in case we have multiple camel contexts 701 // that would have a id clash by auto registering the same bean definition with the same id such as a producer template 702 703 // see if we have already auto registered this id 704 BeanDefinition existing = autoRegisterMap.get(id); 705 if (existing == null) { 706 // no then add it to the map and register it 707 autoRegisterMap.put(id, definition); 708 parserContext.registerComponent(new BeanComponentDefinition(definition, id)); 709 if (LOG.isDebugEnabled()) { 710 LOG.debug("Registered default: {} with id: {} on camel context: {}", definition.getBeanClassName(), id, 711 contextId); 712 } 713 } else { 714 // ups we have already registered it before with same id, but on another camel context 715 // this is not good so we need to remove all traces of this auto registering. 716 // end user must manually add the needed XML elements and provide unique ids access all camel context himself. 717 LOG.debug("Unregistered default: {} with id: {} as we have multiple camel contexts and they must use unique ids." 718 + " You must define the definition in the XML file manually to avoid id clashes when using multiple camel contexts", 719 definition.getBeanClassName(), id); 720 721 parserContext.getRegistry().removeBeanDefinition(id); 722 } 723 } 724 725 private void registerEndpoint(Element childElement, ParserContext parserContext, String contextId) { 726 String id = childElement.getAttribute("id"); 727 // must have an id to be registered 728 if (ObjectHelper.isNotEmpty(id)) { 729 // skip underscore as they are internal naming and should not be registered 730 if (id.startsWith("_")) { 731 LOG.debug("Skip registering endpoint starting with underscore: {}", id); 732 return; 733 } 734 BeanDefinition definition = endpointParser.parse(childElement, parserContext); 735 definition.getPropertyValues().addPropertyValue("camelContext", new RuntimeBeanReference(contextId)); 736 // Need to add this dependency of CamelContext for Spring 3.0 737 try { 738 Method method = definition.getClass().getMethod("setDependsOn", String[].class); 739 method.invoke(definition, (Object) new String[] { contextId }); 740 } catch (Exception e) { 741 // Do nothing here 742 } 743 parserContext.registerBeanComponent(new BeanComponentDefinition(definition, id)); 744 } 745 } 746 747}