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