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.blueprint.handler; 018 019import java.io.UnsupportedEncodingException; 020import java.lang.reflect.Field; 021import java.lang.reflect.Method; 022import java.lang.reflect.Modifier; 023import java.net.URI; 024import java.net.URISyntaxException; 025import java.net.URL; 026import java.util.Arrays; 027import java.util.HashSet; 028import java.util.List; 029import java.util.Map; 030import java.util.Set; 031import java.util.concurrent.Callable; 032 033import javax.xml.bind.Binder; 034import javax.xml.bind.JAXBContext; 035import javax.xml.bind.JAXBException; 036 037import org.apache.camel.blueprint.CamelRouteConfigurationContextFactoryBean; 038import org.apache.camel.builder.LegacyDeadLetterChannelBuilder; 039import org.apache.camel.builder.LegacyDefaultErrorHandlerBuilder; 040import org.apache.camel.builder.LegacyNoErrorHandlerBuilder; 041import org.apache.camel.reifier.errorhandler.ErrorHandlerReifier; 042import org.apache.camel.reifier.errorhandler.LegacyDeadLetterChannelReifier; 043import org.apache.camel.reifier.errorhandler.LegacyDefaultErrorHandlerReifier; 044import org.apache.camel.reifier.errorhandler.LegacyNoErrorHandlerReifier; 045import org.w3c.dom.Document; 046import org.w3c.dom.Element; 047import org.w3c.dom.NamedNodeMap; 048import org.w3c.dom.Node; 049import org.w3c.dom.NodeList; 050 051import org.apache.aries.blueprint.BeanProcessor; 052import org.apache.aries.blueprint.ComponentDefinitionRegistry; 053import org.apache.aries.blueprint.ComponentDefinitionRegistryProcessor; 054import org.apache.aries.blueprint.NamespaceHandler; 055import org.apache.aries.blueprint.ParserContext; 056import org.apache.aries.blueprint.PassThroughMetadata; 057import org.apache.aries.blueprint.mutable.MutableBeanMetadata; 058import org.apache.aries.blueprint.mutable.MutablePassThroughMetadata; 059import org.apache.aries.blueprint.mutable.MutableRefMetadata; 060import org.apache.aries.blueprint.mutable.MutableReferenceMetadata; 061import org.apache.camel.BeanInject; 062import org.apache.camel.CamelContext; 063import org.apache.camel.Endpoint; 064import org.apache.camel.EndpointInject; 065import org.apache.camel.Produce; 066import org.apache.camel.PropertyInject; 067import org.apache.camel.blueprint.BlueprintCamelContext; 068import org.apache.camel.blueprint.BlueprintCamelStateService; 069import org.apache.camel.blueprint.BlueprintModelJAXBContextFactory; 070import org.apache.camel.blueprint.CamelContextFactoryBean; 071import org.apache.camel.blueprint.CamelEndpointFactoryBean; 072import org.apache.camel.blueprint.CamelRestContextFactoryBean; 073import org.apache.camel.blueprint.CamelRouteContextFactoryBean; 074import org.apache.camel.blueprint.CamelRouteTemplateContextFactoryBean; 075import org.apache.camel.core.xml.AbstractCamelFactoryBean; 076import org.apache.camel.impl.engine.CamelPostProcessorHelper; 077import org.apache.camel.impl.engine.DefaultCamelContextNameStrategy; 078import org.apache.camel.model.AggregateDefinition; 079import org.apache.camel.model.CatchDefinition; 080import org.apache.camel.model.DataFormatDefinition; 081import org.apache.camel.model.ExpressionNode; 082import org.apache.camel.model.ExpressionSubElementDefinition; 083import org.apache.camel.model.FromDefinition; 084import org.apache.camel.model.MarshalDefinition; 085import org.apache.camel.model.Model; 086import org.apache.camel.model.OnExceptionDefinition; 087import org.apache.camel.model.ProcessorDefinition; 088import org.apache.camel.model.ResequenceDefinition; 089import org.apache.camel.model.RouteDefinition; 090import org.apache.camel.model.SendDefinition; 091import org.apache.camel.model.SortDefinition; 092import org.apache.camel.model.ToDefinition; 093import org.apache.camel.model.ToDynamicDefinition; 094import org.apache.camel.model.UnmarshalDefinition; 095import org.apache.camel.model.WireTapDefinition; 096import org.apache.camel.model.language.ExpressionDefinition; 097import org.apache.camel.model.rest.RestBindingMode; 098import org.apache.camel.model.rest.RestDefinition; 099import org.apache.camel.model.rest.VerbDefinition; 100import org.apache.camel.spi.CamelContextNameStrategy; 101import org.apache.camel.spi.ComponentResolver; 102import org.apache.camel.spi.DataFormatResolver; 103import org.apache.camel.spi.LanguageResolver; 104import org.apache.camel.spi.NamespaceAware; 105import org.apache.camel.spi.PropertiesComponent; 106import org.apache.camel.support.ObjectHelper; 107import org.apache.camel.support.builder.Namespaces; 108import org.apache.camel.support.builder.xml.NamespacesHelper; 109import org.apache.camel.support.jsse.KeyStoreParameters; 110import org.apache.camel.support.jsse.SSLContextParameters; 111import org.apache.camel.support.jsse.SecureRandomParameters; 112import org.apache.camel.util.StringHelper; 113import org.apache.camel.util.URISupport; 114import org.apache.camel.util.blueprint.KeyStoreParametersFactoryBean; 115import org.apache.camel.util.blueprint.SSLContextParametersFactoryBean; 116import org.apache.camel.util.blueprint.SecureRandomParametersFactoryBean; 117import org.osgi.framework.Bundle; 118import org.osgi.service.blueprint.container.BlueprintContainer; 119import org.osgi.service.blueprint.container.ComponentDefinitionException; 120import org.osgi.service.blueprint.reflect.BeanMetadata; 121import org.osgi.service.blueprint.reflect.ComponentMetadata; 122import org.osgi.service.blueprint.reflect.Metadata; 123import org.osgi.service.blueprint.reflect.RefMetadata; 124import org.slf4j.Logger; 125import org.slf4j.LoggerFactory; 126 127 128import static org.osgi.service.blueprint.reflect.ComponentMetadata.ACTIVATION_LAZY; 129import static org.osgi.service.blueprint.reflect.ServiceReferenceMetadata.AVAILABILITY_MANDATORY; 130import static org.osgi.service.blueprint.reflect.ServiceReferenceMetadata.AVAILABILITY_OPTIONAL; 131 132/** 133 * Camel {@link NamespaceHandler} to parse the Camel related namespaces. 134 */ 135public class CamelNamespaceHandler implements NamespaceHandler { 136 137 static { 138 // legacy camel-blueprint error-handling using its own model and parsers 139 ErrorHandlerReifier.registerReifier(LegacyDeadLetterChannelBuilder.class, LegacyDeadLetterChannelReifier::new); 140 ErrorHandlerReifier.registerReifier(LegacyDefaultErrorHandlerBuilder.class, LegacyDefaultErrorHandlerReifier::new); 141 ErrorHandlerReifier.registerReifier(LegacyNoErrorHandlerBuilder.class, LegacyNoErrorHandlerReifier::new); 142 } 143 144 public static final String BLUEPRINT_NS = "http://camel.apache.org/schema/blueprint"; 145 public static final String SPRING_NS = "http://camel.apache.org/schema/spring"; 146 147 private static final String CAMEL_CONTEXT = "camelContext"; 148 private static final String ROUTE_CONTEXT = "routeContext"; 149 private static final String ROUTE_CONFIGURATION_CONTEXT = "routeConfigurationContext"; 150 private static final String ROUTE_TEMPLATE_CONTEXT = "routeTemplateContext"; 151 private static final String REST_CONTEXT = "restContext"; 152 private static final String ENDPOINT = "endpoint"; 153 private static final String KEY_STORE_PARAMETERS = "keyStoreParameters"; 154 private static final String SECURE_RANDOM_PARAMETERS = "secureRandomParameters"; 155 private static final String SSL_CONTEXT_PARAMETERS = "sslContextParameters"; 156 157 private static final Logger LOG = LoggerFactory.getLogger(CamelNamespaceHandler.class); 158 159 private JAXBContext jaxbContext; 160 161 /** 162 * Prepares the nodes before parsing. 163 */ 164 public static void doBeforeParse(Node node, String fromNamespace, String toNamespace) { 165 if (node.getNodeType() == Node.ELEMENT_NODE) { 166 Document doc = node.getOwnerDocument(); 167 if (node.getNamespaceURI().equals(fromNamespace)) { 168 doc.renameNode(node, toNamespace, node.getLocalName()); 169 } 170 171 // remove whitespace noise from uri, xxxUri attributes, eg new lines, and tabs etc, which allows end users to format 172 // their Camel routes in more human readable format, but at runtime those attributes must be trimmed 173 // the parser removes most of the noise, but keeps double spaces in the attribute values 174 NamedNodeMap map = node.getAttributes(); 175 for (int i = 0; i < map.getLength(); i++) { 176 Node att = map.item(i); 177 if (att.getNodeName().equals("uri") || att.getNodeName().endsWith("Uri")) { 178 final String value = att.getNodeValue(); 179 String before = StringHelper.before(value, "?"); 180 String after = StringHelper.after(value, "?"); 181 182 if (before != null && after != null) { 183 // remove all double spaces in the uri parameters 184 String changed = after.replaceAll("\\s{2,}", ""); 185 if (!after.equals(changed)) { 186 String newAtr = before.trim() + "?" + changed.trim(); 187 LOG.debug("Removed whitespace noise from attribute {} -> {}", value, newAtr); 188 att.setNodeValue(newAtr); 189 } 190 } 191 } 192 } 193 } 194 NodeList list = node.getChildNodes(); 195 for (int i = 0; i < list.getLength(); ++i) { 196 doBeforeParse(list.item(i), fromNamespace, toNamespace); 197 } 198 } 199 200 @Override 201 public URL getSchemaLocation(String namespace) { 202 if (BLUEPRINT_NS.equals(namespace)) { 203 return getClass().getClassLoader().getResource("camel-blueprint.xsd"); 204 } 205 return null; 206 } 207 208 @Override 209 @SuppressWarnings({"rawtypes"}) 210 public Set<Class> getManagedClasses() { 211 return new HashSet<>(Arrays.asList(BlueprintCamelContext.class)); 212 } 213 214 @Override 215 public Metadata parse(Element element, ParserContext context) { 216 LOG.trace("Parsing element {}", element); 217 218 try { 219 // as the camel-core model namespace is Spring we need to rename from blueprint to spring 220 doBeforeParse(element, BLUEPRINT_NS, SPRING_NS); 221 222 if (element.getLocalName().equals(CAMEL_CONTEXT)) { 223 return parseCamelContextNode(element, context); 224 } 225 if (element.getLocalName().equals(ROUTE_CONTEXT)) { 226 return parseRouteContextNode(element, context); 227 } 228 if (element.getLocalName().equals(ROUTE_CONFIGURATION_CONTEXT)) { 229 return parseRouteConfigurationContextNode(element, context); 230 } 231 if (element.getLocalName().equals(ROUTE_TEMPLATE_CONTEXT)) { 232 return parseRouteTemplateContextNode(element, context); 233 } 234 if (element.getLocalName().equals(REST_CONTEXT)) { 235 return parseRestContextNode(element, context); 236 } 237 if (element.getLocalName().equals(ENDPOINT)) { 238 return parseEndpointNode(element, context); 239 } 240 if (element.getLocalName().equals(KEY_STORE_PARAMETERS)) { 241 return parseKeyStoreParametersNode(element, context); 242 } 243 if (element.getLocalName().equals(SECURE_RANDOM_PARAMETERS)) { 244 return parseSecureRandomParametersNode(element, context); 245 } 246 if (element.getLocalName().equals(SSL_CONTEXT_PARAMETERS)) { 247 return parseSSLContextParametersNode(element, context); 248 } 249 } finally { 250 // make sure to rename back so we leave the DOM as-is 251 doBeforeParse(element, SPRING_NS, BLUEPRINT_NS); 252 } 253 254 return null; 255 } 256 257 private Metadata parseCamelContextNode(Element element, ParserContext context) { 258 LOG.trace("Parsing CamelContext {}", element); 259 // Find the id, generate one if needed 260 String contextId = element.getAttribute("id"); 261 boolean implicitId = false; 262 263 // let's avoid folks having to explicitly give an ID to a camel context 264 if (org.apache.camel.util.ObjectHelper.isEmpty(contextId)) { 265 // if no explicit id was set then use a default auto generated name 266 CamelContextNameStrategy strategy = new DefaultCamelContextNameStrategy(); 267 contextId = strategy.getName(); 268 element.setAttributeNS(null, "id", contextId); 269 implicitId = true; 270 } 271 272 // now let's parse the routes with JAXB 273 Binder<Node> binder; 274 try { 275 binder = getJaxbContext().createBinder(); 276 } catch (JAXBException e) { 277 throw new ComponentDefinitionException("Failed to create the JAXB binder : " + e, e); 278 } 279 Object value = parseUsingJaxb(element, context, binder); 280 if (!(value instanceof CamelContextFactoryBean)) { 281 throw new ComponentDefinitionException("Expected an instance of " + CamelContextFactoryBean.class); 282 } 283 284 CamelContextFactoryBean ccfb = (CamelContextFactoryBean) value; 285 ccfb.setImplicitId(implicitId); 286 287 MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class); 288 factory.setId(".camelBlueprint.passThrough." + contextId); 289 factory.setObject(new PassThroughCallable<>(value)); 290 291 MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class); 292 factory2.setId(".camelBlueprint.factory." + contextId); 293 factory2.setFactoryComponent(factory); 294 factory2.setFactoryMethod("call"); 295 factory2.setInitMethod("afterPropertiesSet"); 296 factory2.setDestroyMethod("destroy"); 297 factory2.addProperty("blueprintContainer", createRef(context, "blueprintContainer")); 298 factory2.addProperty("bundleContext", createRef(context, "blueprintBundleContext")); 299 // We need to add other components which the camel context dependsOn 300 if (org.apache.camel.util.ObjectHelper.isNotEmpty(ccfb.getDependsOn())) { 301 factory2.setDependsOn(Arrays.asList(ccfb.getDependsOn().split(" |,"))); 302 } 303 context.getComponentDefinitionRegistry().registerComponentDefinition(factory2); 304 305 MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class); 306 ctx.setId(contextId); 307 ctx.setRuntimeClass(BlueprintCamelContext.class); 308 ctx.setFactoryComponent(factory2); 309 ctx.setFactoryMethod("getContext"); 310 ctx.addProperty("bundleStateService", createRef(context, ".camelBlueprint.bundleStateService")); 311 ctx.setInitMethod("build"); 312 ctx.setDestroyMethod("destroy"); 313 314 // Register factory beans 315 registerBeans(context, contextId, ccfb.getThreadPools()); 316 registerBeans(context, contextId, ccfb.getEndpoints()); 317 registerBeans(context, contextId, ccfb.getRedeliveryPolicies()); 318 registerBeans(context, contextId, ccfb.getBeansFactory()); 319 320 // Register single CamelBundleStateService - shared for all bundles and all Blueprint Camel contexts 321 registerBundleStateService(context); 322 323 // Register processors 324 MutablePassThroughMetadata beanProcessorFactory = context.createMetadata(MutablePassThroughMetadata.class); 325 beanProcessorFactory.setId(".camelBlueprint.processor.bean.passThrough." + contextId); 326 beanProcessorFactory.setObject(new PassThroughCallable<Object>(new CamelInjector(contextId))); 327 328 MutableBeanMetadata beanProcessor = context.createMetadata(MutableBeanMetadata.class); 329 beanProcessor.setId(".camelBlueprint.processor.bean." + contextId); 330 beanProcessor.setRuntimeClass(CamelInjector.class); 331 beanProcessor.setFactoryComponent(beanProcessorFactory); 332 beanProcessor.setFactoryMethod("call"); 333 beanProcessor.setProcessor(true); 334 beanProcessor.addProperty("blueprintContainer", createRef(context, "blueprintContainer")); 335 context.getComponentDefinitionRegistry().registerComponentDefinition(beanProcessor); 336 337 MutablePassThroughMetadata regProcessorFactory = context.createMetadata(MutablePassThroughMetadata.class); 338 regProcessorFactory.setId(".camelBlueprint.processor.registry.passThrough." + contextId); 339 regProcessorFactory.setObject(new PassThroughCallable<Object>(new CamelDependenciesFinder(contextId, context))); 340 341 MutableBeanMetadata regProcessor = context.createMetadata(MutableBeanMetadata.class); 342 regProcessor.setId(".camelBlueprint.processor.registry." + contextId); 343 regProcessor.setRuntimeClass(CamelDependenciesFinder.class); 344 regProcessor.setFactoryComponent(regProcessorFactory); 345 regProcessor.setFactoryMethod("call"); 346 regProcessor.setProcessor(true); 347 regProcessor.addDependsOn(".camelBlueprint.processor.bean." + contextId); 348 regProcessor.addProperty("blueprintContainer", createRef(context, "blueprintContainer")); 349 context.getComponentDefinitionRegistry().registerComponentDefinition(regProcessor); 350 351 // lets inject the namespaces into any namespace aware POJOs 352 injectNamespaces(element, binder); 353 354 LOG.trace("Parsing CamelContext done, returning {}", ctx); 355 return ctx; 356 } 357 358 protected void injectNamespaces(Element element, Binder<Node> binder) { 359 NodeList list = element.getChildNodes(); 360 Namespaces namespaces = null; 361 int size = list.getLength(); 362 for (int i = 0; i < size; i++) { 363 Node child = list.item(i); 364 if (child instanceof Element) { 365 Element childElement = (Element) child; 366 Object object = binder.getJAXBNode(child); 367 if (object instanceof NamespaceAware) { 368 NamespaceAware namespaceAware = (NamespaceAware) object; 369 if (namespaces == null) { 370 namespaces = NamespacesHelper.namespaces(element); 371 } 372 namespaces.configure(namespaceAware); 373 } 374 injectNamespaces(childElement, binder); 375 } 376 } 377 } 378 379 private Metadata parseRouteContextNode(Element element, ParserContext context) { 380 LOG.trace("Parsing RouteContext {}", element); 381 // now parse the routes with JAXB 382 Binder<Node> binder; 383 try { 384 binder = getJaxbContext().createBinder(); 385 } catch (JAXBException e) { 386 throw new ComponentDefinitionException("Failed to create the JAXB binder: " + e, e); 387 } 388 Object value = parseUsingJaxb(element, context, binder); 389 if (!(value instanceof CamelRouteContextFactoryBean)) { 390 throw new ComponentDefinitionException("Expected an instance of " + CamelRouteContextFactoryBean.class); 391 } 392 393 CamelRouteContextFactoryBean rcfb = (CamelRouteContextFactoryBean) value; 394 String id = rcfb.getId(); 395 396 MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class); 397 factory.setId(".camelBlueprint.passThrough." + id); 398 factory.setObject(new PassThroughCallable<Object>(rcfb)); 399 400 MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class); 401 factory2.setId(".camelBlueprint.factory." + id); 402 factory2.setFactoryComponent(factory); 403 factory2.setFactoryMethod("call"); 404 405 MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class); 406 ctx.setId(id); 407 ctx.setRuntimeClass(List.class); 408 ctx.setFactoryComponent(factory2); 409 ctx.setFactoryMethod("getRoutes"); 410 // must be lazy as we want CamelContext to be activated first 411 ctx.setActivation(ACTIVATION_LAZY); 412 413 // lets inject the namespaces into any namespace aware POJOs 414 injectNamespaces(element, binder); 415 416 LOG.trace("Parsing RouteContext {} done, returning {}", element, ctx); 417 return ctx; 418 } 419 420 private Metadata parseRouteConfigurationContextNode(Element element, ParserContext context) { 421 LOG.trace("Parsing RouteConfigurationContext {}", element); 422 // now parse the routes with JAXB 423 Binder<Node> binder; 424 try { 425 binder = getJaxbContext().createBinder(); 426 } catch (JAXBException e) { 427 throw new ComponentDefinitionException("Failed to create the JAXB binder: " + e, e); 428 } 429 Object value = parseUsingJaxb(element, context, binder); 430 if (!(value instanceof CamelRouteConfigurationContextFactoryBean)) { 431 throw new ComponentDefinitionException("Expected an instance of " + CamelRouteConfigurationContextFactoryBean.class); 432 } 433 434 CamelRouteConfigurationContextFactoryBean rcfb = (CamelRouteConfigurationContextFactoryBean) value; 435 String id = rcfb.getId(); 436 437 MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class); 438 factory.setId(".camelBlueprint.passThrough." + id); 439 factory.setObject(new PassThroughCallable<Object>(rcfb)); 440 441 MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class); 442 factory2.setId(".camelBlueprint.factory." + id); 443 factory2.setFactoryComponent(factory); 444 factory2.setFactoryMethod("call"); 445 446 MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class); 447 ctx.setId(id); 448 ctx.setRuntimeClass(List.class); 449 ctx.setFactoryComponent(factory2); 450 ctx.setFactoryMethod("getRouteConfigurations"); 451 // must be lazy as we want CamelContext to be activated first 452 ctx.setActivation(ACTIVATION_LAZY); 453 454 // lets inject the namespaces into any namespace aware POJOs 455 injectNamespaces(element, binder); 456 457 LOG.trace("Parsing RouteConfigurationContext {} done, returning {}", element, ctx); 458 return ctx; 459 } 460 461 private Metadata parseRouteTemplateContextNode(Element element, ParserContext context) { 462 LOG.trace("Parsing RouteTemplateContext {}", element); 463 // now parse the routes with JAXB 464 Binder<Node> binder; 465 try { 466 binder = getJaxbContext().createBinder(); 467 } catch (JAXBException e) { 468 throw new ComponentDefinitionException("Failed to create the JAXB binder: " + e, e); 469 } 470 Object value = parseUsingJaxb(element, context, binder); 471 if (!(value instanceof CamelRouteTemplateContextFactoryBean)) { 472 throw new ComponentDefinitionException("Expected an instance of " + CamelRouteTemplateContextFactoryBean.class); 473 } 474 475 CamelRouteTemplateContextFactoryBean rcfb = (CamelRouteTemplateContextFactoryBean) value; 476 String id = rcfb.getId(); 477 478 MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class); 479 factory.setId(".camelBlueprint.passThrough." + id); 480 factory.setObject(new PassThroughCallable<Object>(rcfb)); 481 482 MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class); 483 factory2.setId(".camelBlueprint.factory." + id); 484 factory2.setFactoryComponent(factory); 485 factory2.setFactoryMethod("call"); 486 487 MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class); 488 ctx.setId(id); 489 ctx.setRuntimeClass(List.class); 490 ctx.setFactoryComponent(factory2); 491 ctx.setFactoryMethod("getRouteTemplates"); 492 // must be lazy as we want CamelContext to be activated first 493 ctx.setActivation(ACTIVATION_LAZY); 494 495 // lets inject the namespaces into any namespace aware POJOs 496 injectNamespaces(element, binder); 497 498 LOG.trace("Parsing RouteTemplateContext {} done, returning {}", element, ctx); 499 return ctx; 500 } 501 502 private Metadata parseRestContextNode(Element element, ParserContext context) { 503 LOG.trace("Parsing RestContext {}", element); 504 // now parse the rests with JAXB 505 Binder<Node> binder; 506 try { 507 binder = getJaxbContext().createBinder(); 508 } catch (JAXBException e) { 509 throw new ComponentDefinitionException("Failed to create the JAXB binder: " + e, e); 510 } 511 Object value = parseUsingJaxb(element, context, binder); 512 if (!(value instanceof CamelRestContextFactoryBean)) { 513 throw new ComponentDefinitionException("Expected an instance of " + CamelRestContextFactoryBean.class); 514 } 515 516 CamelRestContextFactoryBean rcfb = (CamelRestContextFactoryBean) value; 517 String id = rcfb.getId(); 518 519 MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class); 520 factory.setId(".camelBlueprint.passThrough." + id); 521 factory.setObject(new PassThroughCallable<Object>(rcfb)); 522 523 MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class); 524 factory2.setId(".camelBlueprint.factory." + id); 525 factory2.setFactoryComponent(factory); 526 factory2.setFactoryMethod("call"); 527 528 MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class); 529 ctx.setId(id); 530 ctx.setRuntimeClass(List.class); 531 ctx.setFactoryComponent(factory2); 532 ctx.setFactoryMethod("getRests"); 533 // must be lazy as we want CamelContext to be activated first 534 ctx.setActivation(ACTIVATION_LAZY); 535 536 // lets inject the namespaces into any namespace aware POJOs 537 injectNamespaces(element, binder); 538 539 LOG.trace("Parsing RestContext {} done, returning {}", element, ctx); 540 return ctx; 541 } 542 543 private Metadata parseEndpointNode(Element element, ParserContext context) { 544 LOG.trace("Parsing Endpoint {}", element); 545 // now parse the rests with JAXB 546 Binder<Node> binder; 547 try { 548 binder = getJaxbContext().createBinder(); 549 } catch (JAXBException e) { 550 throw new ComponentDefinitionException("Failed to create the JAXB binder: " + e, e); 551 } 552 Object value = parseUsingJaxb(element, context, binder); 553 if (!(value instanceof CamelEndpointFactoryBean)) { 554 throw new ComponentDefinitionException("Expected an instance of " + CamelEndpointFactoryBean.class); 555 } 556 557 CamelEndpointFactoryBean rcfb = (CamelEndpointFactoryBean) value; 558 String id = rcfb.getId(); 559 560 MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class); 561 factory.setId(".camelBlueprint.passThrough." + id); 562 factory.setObject(new PassThroughCallable<Object>(rcfb)); 563 564 MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class); 565 factory2.setId(".camelBlueprint.factory." + id); 566 factory2.setFactoryComponent(factory); 567 factory2.setFactoryMethod("call"); 568 factory2.setInitMethod("afterPropertiesSet"); 569 factory2.setDestroyMethod("destroy"); 570 factory2.addProperty("blueprintContainer", createRef(context, "blueprintContainer")); 571 572 MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class); 573 ctx.setId(id); 574 ctx.setRuntimeClass(Endpoint.class); 575 ctx.setFactoryComponent(factory2); 576 ctx.setFactoryMethod("getObject"); 577 // must be lazy as we want CamelContext to be activated first 578 ctx.setActivation(ACTIVATION_LAZY); 579 580 LOG.trace("Parsing endpoint {} done, returning {}", element, ctx); 581 return ctx; 582 } 583 584 private Metadata parseKeyStoreParametersNode(Element element, ParserContext context) { 585 LOG.trace("Parsing KeyStoreParameters {}", element); 586 // now parse the key store parameters with JAXB 587 Binder<Node> binder; 588 try { 589 binder = getJaxbContext().createBinder(); 590 } catch (JAXBException e) { 591 throw new ComponentDefinitionException("Failed to create the JAXB binder: " + e, e); 592 } 593 Object value = parseUsingJaxb(element, context, binder); 594 if (!(value instanceof KeyStoreParametersFactoryBean)) { 595 throw new ComponentDefinitionException("Expected an instance of " + KeyStoreParametersFactoryBean.class); 596 } 597 598 KeyStoreParametersFactoryBean kspfb = (KeyStoreParametersFactoryBean) value; 599 String id = kspfb.getId(); 600 601 MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class); 602 factory.setId(".camelBlueprint.passThrough." + id); 603 factory.setObject(new PassThroughCallable<Object>(kspfb)); 604 605 MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class); 606 factory2.setId(".camelBlueprint.factory." + id); 607 factory2.setFactoryComponent(factory); 608 factory2.setFactoryMethod("call"); 609 factory2.setInitMethod("afterPropertiesSet"); 610 factory2.setDestroyMethod("destroy"); 611 factory2.addProperty("blueprintContainer", createRef(context, "blueprintContainer")); 612 613 MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class); 614 ctx.setId(id); 615 ctx.setRuntimeClass(KeyStoreParameters.class); 616 ctx.setFactoryComponent(factory2); 617 ctx.setFactoryMethod("getObject"); 618 // must be lazy as we want CamelContext to be activated first 619 ctx.setActivation(ACTIVATION_LAZY); 620 621 LOG.trace("Parsing KeyStoreParameters done, returning {}", ctx); 622 return ctx; 623 } 624 625 private Metadata parseSecureRandomParametersNode(Element element, ParserContext context) { 626 LOG.trace("Parsing SecureRandomParameters {}", element); 627 // now parse the key store parameters with JAXB 628 Binder<Node> binder; 629 try { 630 binder = getJaxbContext().createBinder(); 631 } catch (JAXBException e) { 632 throw new ComponentDefinitionException("Failed to create the JAXB binder: " + e, e); 633 } 634 Object value = parseUsingJaxb(element, context, binder); 635 if (!(value instanceof SecureRandomParametersFactoryBean)) { 636 throw new ComponentDefinitionException("Expected an instance of " + SecureRandomParametersFactoryBean.class); 637 } 638 639 SecureRandomParametersFactoryBean srfb = (SecureRandomParametersFactoryBean) value; 640 String id = srfb.getId(); 641 642 MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class); 643 factory.setId(".camelBlueprint.passThrough." + id); 644 factory.setObject(new PassThroughCallable<Object>(srfb)); 645 646 MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class); 647 factory2.setId(".camelBlueprint.factory." + id); 648 factory2.setFactoryComponent(factory); 649 factory2.setFactoryMethod("call"); 650 factory2.setInitMethod("afterPropertiesSet"); 651 factory2.setDestroyMethod("destroy"); 652 factory2.addProperty("blueprintContainer", createRef(context, "blueprintContainer")); 653 654 MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class); 655 ctx.setId(id); 656 ctx.setRuntimeClass(SecureRandomParameters.class); 657 ctx.setFactoryComponent(factory2); 658 ctx.setFactoryMethod("getObject"); 659 // must be lazy as we want CamelContext to be activated first 660 ctx.setActivation(ACTIVATION_LAZY); 661 662 LOG.trace("Parsing SecureRandomParameters done, returning {}", ctx); 663 return ctx; 664 } 665 666 private Metadata parseSSLContextParametersNode(Element element, ParserContext context) { 667 LOG.trace("Parsing SSLContextParameters {}", element); 668 // now parse the key store parameters with JAXB 669 Binder<Node> binder; 670 try { 671 binder = getJaxbContext().createBinder(); 672 } catch (JAXBException e) { 673 throw new ComponentDefinitionException("Failed to create the JAXB binder: " + e, e); 674 } 675 Object value = parseUsingJaxb(element, context, binder); 676 if (!(value instanceof SSLContextParametersFactoryBean)) { 677 throw new ComponentDefinitionException("Expected an instance of " + SSLContextParametersFactoryBean.class); 678 } 679 680 SSLContextParametersFactoryBean scpfb = (SSLContextParametersFactoryBean) value; 681 String id = scpfb.getId(); 682 683 MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class); 684 factory.setId(".camelBlueprint.passThrough." + id); 685 factory.setObject(new PassThroughCallable<Object>(scpfb)); 686 687 MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class); 688 factory2.setId(".camelBlueprint.factory." + id); 689 factory2.setFactoryComponent(factory); 690 factory2.setFactoryMethod("call"); 691 factory2.setInitMethod("afterPropertiesSet"); 692 factory2.setDestroyMethod("destroy"); 693 factory2.addProperty("blueprintContainer", createRef(context, "blueprintContainer")); 694 695 MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class); 696 ctx.setId(id); 697 ctx.setRuntimeClass(SSLContextParameters.class); 698 ctx.setFactoryComponent(factory2); 699 ctx.setFactoryMethod("getObject"); 700 // must be lazy as we want CamelContext to be activated first 701 ctx.setActivation(ACTIVATION_LAZY); 702 703 LOG.trace("Parsing SSLContextParameters done, returning {}", ctx); 704 return ctx; 705 } 706 707 private void registerBeans(ParserContext context, String contextId, List<?> beans) { 708 if (beans != null) { 709 for (Object bean : beans) { 710 if (bean instanceof AbstractCamelFactoryBean) { 711 registerBean(context, contextId, (AbstractCamelFactoryBean<?>) bean); 712 } 713 } 714 } 715 } 716 717 protected void registerBean(ParserContext context, String contextId, AbstractCamelFactoryBean<?> fact) { 718 String id = fact.getId(); 719 720 fact.setCamelContextId(contextId); 721 722 MutablePassThroughMetadata eff = context.createMetadata(MutablePassThroughMetadata.class); 723 eff.setId(".camelBlueprint.bean.passthrough." + id); 724 eff.setObject(new PassThroughCallable<Object>(fact)); 725 726 MutableBeanMetadata ef = context.createMetadata(MutableBeanMetadata.class); 727 ef.setId(".camelBlueprint.bean.factory." + id); 728 ef.setFactoryComponent(eff); 729 ef.setFactoryMethod("call"); 730 ef.addProperty("blueprintContainer", createRef(context, "blueprintContainer")); 731 ef.setInitMethod("afterPropertiesSet"); 732 ef.setDestroyMethod("destroy"); 733 734 MutableBeanMetadata e = context.createMetadata(MutableBeanMetadata.class); 735 e.setId(id); 736 e.setRuntimeClass(fact.getObjectType()); 737 e.setFactoryComponent(ef); 738 e.setFactoryMethod("getObject"); 739 e.addDependsOn(".camelBlueprint.processor.bean." + contextId); 740 741 context.getComponentDefinitionRegistry().registerComponentDefinition(e); 742 } 743 744 /** 745 * There's single instance of {@link BlueprintCamelStateService} that's used by all Blueprint Camel contexts 746 * to inform about state of Camel contexts. If Karaf is available, this information will propagate to 747 * <em>extended bundle info</em>. 748 * See CAMEL-12980 749 */ 750 private void registerBundleStateService(ParserContext context) { 751 ComponentDefinitionRegistry componentDefinitionRegistry = context.getComponentDefinitionRegistry(); 752 ComponentMetadata cm = componentDefinitionRegistry.getComponentDefinition(".camelBlueprint.bundleStateService"); 753 if (cm == null) { 754 MutableBeanMetadata ssm = context.createMetadata(MutableBeanMetadata.class); 755 ssm.setId(".camelBlueprint.bundleStateService"); 756 ssm.setRuntimeClass(BlueprintCamelStateService.class); 757 ssm.addProperty("bundleContext", createRef(context, "blueprintBundleContext")); 758 ssm.setInitMethod("init"); 759 ssm.setDestroyMethod("destroy"); 760 componentDefinitionRegistry.registerComponentDefinition(ssm); 761 } 762 } 763 764 protected BlueprintContainer getBlueprintContainer(ParserContext context) { 765 PassThroughMetadata ptm = (PassThroughMetadata) context.getComponentDefinitionRegistry().getComponentDefinition("blueprintContainer"); 766 return (BlueprintContainer) ptm.getObject(); 767 } 768 769 @Override 770 public ComponentMetadata decorate(Node node, ComponentMetadata component, ParserContext context) { 771 return null; 772 } 773 774 protected Object parseUsingJaxb(Element element, ParserContext parserContext, Binder<Node> binder) { 775 try { 776 return binder.unmarshal(element); 777 } catch (JAXBException e) { 778 throw new ComponentDefinitionException("Failed to parse JAXB element: " + e, e); 779 } 780 } 781 782 public JAXBContext getJaxbContext() throws JAXBException { 783 if (jaxbContext == null) { 784 jaxbContext = new BlueprintModelJAXBContextFactory(getClass().getClassLoader()).newJAXBContext(); 785 } 786 return jaxbContext; 787 } 788 789 private RefMetadata createRef(ParserContext context, String value) { 790 MutableRefMetadata r = context.createMetadata(MutableRefMetadata.class); 791 r.setComponentId(value); 792 return r; 793 } 794 795 private static ComponentMetadata getDataformatResolverReference(ParserContext context, String dataformat) { 796 // we cannot resolve dataformat names using property placeholders at this point in time 797 if (dataformat.startsWith(PropertiesComponent.PREFIX_TOKEN)) { 798 return null; 799 } 800 ComponentDefinitionRegistry componentDefinitionRegistry = context.getComponentDefinitionRegistry(); 801 ComponentMetadata cm = componentDefinitionRegistry.getComponentDefinition(".camelBlueprint.dataformatResolver." + dataformat); 802 if (cm == null) { 803 MutableReferenceMetadata svc = context.createMetadata(MutableReferenceMetadata.class); 804 svc.setId(".camelBlueprint.dataformatResolver." + dataformat); 805 svc.setFilter("(dataformat=" + dataformat + ")"); 806 svc.setAvailability(componentDefinitionRegistry.containsComponentDefinition(dataformat) ? AVAILABILITY_OPTIONAL : AVAILABILITY_MANDATORY); 807 try { 808 // Try to set the runtime interface (only with aries blueprint > 0.1 809 svc.getClass().getMethod("setRuntimeInterface", Class.class).invoke(svc, DataFormatResolver.class); 810 } catch (Throwable t) { 811 // Check if the bundle can see the class 812 try { 813 PassThroughMetadata ptm = (PassThroughMetadata) componentDefinitionRegistry.getComponentDefinition("blueprintBundle"); 814 Bundle b = (Bundle) ptm.getObject(); 815 if (b.loadClass(DataFormatResolver.class.getName()) != DataFormatResolver.class) { 816 throw new UnsupportedOperationException(); 817 } 818 svc.setInterface(DataFormatResolver.class.getName()); 819 } catch (Throwable t2) { 820 throw new UnsupportedOperationException(); 821 } 822 } 823 componentDefinitionRegistry.registerComponentDefinition(svc); 824 cm = svc; 825 } 826 return cm; 827 } 828 829 private static ComponentMetadata getLanguageResolverReference(ParserContext context, String language) { 830 // we cannot resolve language names using property placeholders at this point in time 831 if (language.startsWith(PropertiesComponent.PREFIX_TOKEN)) { 832 return null; 833 } 834 ComponentDefinitionRegistry componentDefinitionRegistry = context.getComponentDefinitionRegistry(); 835 ComponentMetadata cm = componentDefinitionRegistry.getComponentDefinition(".camelBlueprint.languageResolver." + language); 836 if (cm == null) { 837 MutableReferenceMetadata svc = context.createMetadata(MutableReferenceMetadata.class); 838 svc.setId(".camelBlueprint.languageResolver." + language); 839 svc.setFilter("(language=" + language + ")"); 840 svc.setAvailability(componentDefinitionRegistry.containsComponentDefinition(language) ? AVAILABILITY_OPTIONAL : AVAILABILITY_MANDATORY); 841 try { 842 // Try to set the runtime interface (only with aries blueprint > 0.1 843 svc.getClass().getMethod("setRuntimeInterface", Class.class).invoke(svc, LanguageResolver.class); 844 } catch (Throwable t) { 845 // Check if the bundle can see the class 846 try { 847 PassThroughMetadata ptm = (PassThroughMetadata) componentDefinitionRegistry.getComponentDefinition("blueprintBundle"); 848 Bundle b = (Bundle) ptm.getObject(); 849 if (b.loadClass(LanguageResolver.class.getName()) != LanguageResolver.class) { 850 throw new UnsupportedOperationException(); 851 } 852 svc.setInterface(LanguageResolver.class.getName()); 853 } catch (Throwable t2) { 854 throw new UnsupportedOperationException(); 855 } 856 } 857 componentDefinitionRegistry.registerComponentDefinition(svc); 858 cm = svc; 859 } 860 return cm; 861 } 862 863 private static ComponentMetadata getComponentResolverReference(ParserContext context, String component) { 864 // we cannot resolve component names using property placeholders at this point in time 865 if (component.startsWith(PropertiesComponent.PREFIX_TOKEN)) { 866 return null; 867 } 868 ComponentDefinitionRegistry componentDefinitionRegistry = context.getComponentDefinitionRegistry(); 869 ComponentMetadata cm = componentDefinitionRegistry.getComponentDefinition(".camelBlueprint.componentResolver." + component); 870 if (cm == null) { 871 MutableReferenceMetadata svc = context.createMetadata(MutableReferenceMetadata.class); 872 svc.setId(".camelBlueprint.componentResolver." + component); 873 svc.setFilter("(component=" + component + ")"); 874 svc.setAvailability(componentDefinitionRegistry.containsComponentDefinition(component) ? AVAILABILITY_OPTIONAL : AVAILABILITY_MANDATORY); 875 try { 876 // Try to set the runtime interface (only with aries blueprint > 0.1 877 svc.getClass().getMethod("setRuntimeInterface", Class.class).invoke(svc, ComponentResolver.class); 878 } catch (Throwable t) { 879 // Check if the bundle can see the class 880 try { 881 PassThroughMetadata ptm = (PassThroughMetadata) componentDefinitionRegistry.getComponentDefinition("blueprintBundle"); 882 Bundle b = (Bundle) ptm.getObject(); 883 if (b.loadClass(ComponentResolver.class.getName()) != ComponentResolver.class) { 884 throw new UnsupportedOperationException(); 885 } 886 svc.setInterface(ComponentResolver.class.getName()); 887 } catch (Throwable t2) { 888 throw new UnsupportedOperationException(); 889 } 890 } 891 componentDefinitionRegistry.registerComponentDefinition(svc); 892 cm = svc; 893 } 894 return cm; 895 } 896 897 public static class PassThroughCallable<T> implements Callable<T> { 898 899 private T value; 900 901 public PassThroughCallable(T value) { 902 this.value = value; 903 } 904 905 @Override 906 public T call() throws Exception { 907 return value; 908 } 909 } 910 911 public static class CamelInjector extends CamelPostProcessorHelper implements BeanProcessor { 912 913 private final String camelContextName; 914 private BlueprintContainer blueprintContainer; 915 916 public CamelInjector(String camelContextName) { 917 this.camelContextName = camelContextName; 918 } 919 920 public void setBlueprintContainer(BlueprintContainer blueprintContainer) { 921 this.blueprintContainer = blueprintContainer; 922 } 923 924 @Override 925 public CamelContext getCamelContext() { 926 if (blueprintContainer != null) { 927 CamelContext answer = (CamelContext) blueprintContainer.getComponentInstance(camelContextName); 928 return answer; 929 } 930 return null; 931 } 932 933 @Override 934 public Object beforeInit(Object bean, String beanName, BeanCreator beanCreator, BeanMetadata beanMetadata) { 935 LOG.trace("Before init of bean: {} -> {}", beanName, bean); 936 // prefer to inject later in afterInit 937 return bean; 938 } 939 940 /** 941 * A strategy method to allow implementations to perform some custom JBI 942 * based injection of the POJO 943 * 944 * @param bean the bean to be injected 945 */ 946 protected void injectFields(final Object bean, final String beanName) { 947 Class<?> clazz = bean.getClass(); 948 do { 949 Field[] fields = clazz.getDeclaredFields(); 950 for (Field field : fields) { 951 PropertyInject propertyInject = field.getAnnotation(PropertyInject.class); 952 if (propertyInject != null) { 953 injectFieldProperty(field, propertyInject.value(), propertyInject.defaultValue(), bean, beanName); 954 } 955 956 BeanInject beanInject = field.getAnnotation(BeanInject.class); 957 if (beanInject != null) { 958 injectFieldBean(field, beanInject.value(), bean, beanName); 959 } 960 961 EndpointInject endpointInject = field.getAnnotation(EndpointInject.class); 962 if (endpointInject != null) { 963 String uri = endpointInject.value().isEmpty() ? endpointInject.uri() : endpointInject.value(); 964 injectField(field, uri, endpointInject.property(), bean, beanName); 965 } 966 967 Produce produce = field.getAnnotation(Produce.class); 968 if (produce != null) { 969 String uri = produce.value().isEmpty() ? produce.uri() : produce.value(); 970 injectField(field, uri, produce.property(), bean, beanName); 971 } 972 } 973 clazz = clazz.getSuperclass(); 974 } while (clazz != null && clazz != Object.class); 975 } 976 977 protected void injectField(Field field, String endpointUri, String endpointProperty, Object bean, String beanName) { 978 setField(field, bean, getInjectionValue(field.getType(), endpointUri, endpointProperty, field.getName(), bean, beanName)); 979 } 980 981 protected void injectFieldProperty(Field field, String propertyName, String propertyDefaultValue, Object bean, String beanName) { 982 setField(field, bean, getInjectionPropertyValue(field.getType(), propertyName, propertyDefaultValue, field.getName(), bean, beanName)); 983 } 984 985 public void injectFieldBean(Field field, String name, Object bean, String beanName) { 986 setField(field, bean, getInjectionBeanValue(field.getType(), name)); 987 } 988 989 protected static void setField(Field field, Object instance, Object value) { 990 try { 991 boolean oldAccessible = field.isAccessible(); 992 boolean shouldSetAccessible = !Modifier.isPublic(field.getModifiers()) && !oldAccessible; 993 if (shouldSetAccessible) { 994 field.setAccessible(true); 995 } 996 field.set(instance, value); 997 if (shouldSetAccessible) { 998 field.setAccessible(oldAccessible); 999 } 1000 } catch (IllegalArgumentException ex) { 1001 throw new UnsupportedOperationException("Cannot inject value of class: " + value.getClass() + " into: " + field); 1002 } catch (IllegalAccessException ex) { 1003 throw new IllegalStateException("Could not access method: " + ex.getMessage()); 1004 } 1005 } 1006 1007 protected void injectMethods(final Object bean, final String beanName) { 1008 Class<?> clazz = bean.getClass(); 1009 do { 1010 Method[] methods = clazz.getDeclaredMethods(); 1011 for (Method method : methods) { 1012 setterInjection(method, bean, beanName); 1013 consumerInjection(method, bean, beanName); 1014 } 1015 clazz = clazz.getSuperclass(); 1016 } while (clazz != null && clazz != Object.class); 1017 } 1018 1019 protected void setterInjection(Method method, Object bean, String beanName) { 1020 PropertyInject propertyInject = method.getAnnotation(PropertyInject.class); 1021 if (propertyInject != null) { 1022 setterPropertyInjection(method, propertyInject.value(), propertyInject.defaultValue(), bean, beanName); 1023 } 1024 1025 BeanInject beanInject = method.getAnnotation(BeanInject.class); 1026 if (beanInject != null) { 1027 setterBeanInjection(method, beanInject.value(), bean, beanName); 1028 } 1029 1030 EndpointInject endpointInject = method.getAnnotation(EndpointInject.class); 1031 if (endpointInject != null) { 1032 String uri = endpointInject.value().isEmpty() ? endpointInject.uri() : endpointInject.value(); 1033 setterInjection(method, bean, beanName, uri, endpointInject.property()); 1034 } 1035 1036 Produce produce = method.getAnnotation(Produce.class); 1037 if (produce != null) { 1038 String uri = produce.value().isEmpty() ? produce.uri() : produce.value(); 1039 setterInjection(method, bean, beanName, uri, produce.property()); 1040 } 1041 } 1042 1043 protected void setterPropertyInjection(Method method, String propertyValue, String propertyDefaultValue, Object bean, String beanName) { 1044 Class<?>[] parameterTypes = method.getParameterTypes(); 1045 if (parameterTypes.length != 1) { 1046 LOG.warn("Ignoring badly annotated method for injection due to incorrect number of parameters: {}", method); 1047 } else { 1048 String propertyName = org.apache.camel.util.ObjectHelper.getPropertyName(method); 1049 Object value = getInjectionPropertyValue(parameterTypes[0], propertyValue, propertyDefaultValue, propertyName, bean, beanName); 1050 ObjectHelper.invokeMethod(method, bean, value); 1051 } 1052 } 1053 1054 protected void setterBeanInjection(Method method, String name, Object bean, String beanName) { 1055 Class<?>[] parameterTypes = method.getParameterTypes(); 1056 if (parameterTypes.length != 1) { 1057 LOG.warn("Ignoring badly annotated method for injection due to incorrect number of parameters: {}", method); 1058 } else { 1059 Object value = getInjectionBeanValue(parameterTypes[0], name); 1060 ObjectHelper.invokeMethod(method, bean, value); 1061 } 1062 } 1063 1064 protected void setterInjection(Method method, Object bean, String beanName, String endpointUri, String endpointProperty) { 1065 Class<?>[] parameterTypes = method.getParameterTypes(); 1066 if (parameterTypes.length != 1) { 1067 LOG.warn("Ignoring badly annotated method for injection due to incorrect number of parameters: {}", method); 1068 } else { 1069 String propertyName = org.apache.camel.util.ObjectHelper.getPropertyName(method); 1070 Object value = getInjectionValue(parameterTypes[0], endpointUri, endpointProperty, propertyName, bean, beanName); 1071 ObjectHelper.invokeMethod(method, bean, value); 1072 } 1073 } 1074 1075 @Override 1076 public Object afterInit(Object bean, String beanName, BeanCreator beanCreator, BeanMetadata beanMetadata) { 1077 LOG.trace("After init of bean: {} -> {}", beanName, bean); 1078 // we cannot inject CamelContextAware beans as the CamelContext may not be ready 1079 // TODO: use bean post processor instead 1080 injectFields(bean, beanName); 1081 injectMethods(bean, beanName); 1082 return bean; 1083 } 1084 1085 @Override 1086 public void beforeDestroy(Object bean, String beanName) { 1087 } 1088 1089 @Override 1090 public void afterDestroy(Object bean, String beanName) { 1091 } 1092 1093 @Override 1094 protected boolean isSingleton(Object bean, String beanName) { 1095 if (beanName != null) { 1096 ComponentMetadata meta = blueprintContainer.getComponentMetadata(beanName); 1097 if (meta instanceof BeanMetadata) { 1098 String scope = ((BeanMetadata) meta).getScope(); 1099 if (scope != null) { 1100 return BeanMetadata.SCOPE_SINGLETON.equals(scope); 1101 } 1102 } 1103 } 1104 // fallback to super, which will assume singleton 1105 // for beans not implementing Camel's IsSingleton interface 1106 return super.isSingleton(bean, beanName); 1107 } 1108 } 1109 1110 public static class CamelDependenciesFinder implements ComponentDefinitionRegistryProcessor { 1111 1112 private final String camelContextName; 1113 private final ParserContext context; 1114 private BlueprintContainer blueprintContainer; 1115 1116 public CamelDependenciesFinder(String camelContextName, ParserContext context) { 1117 this.camelContextName = camelContextName; 1118 this.context = context; 1119 } 1120 1121 public void setBlueprintContainer(BlueprintContainer blueprintContainer) { 1122 this.blueprintContainer = blueprintContainer; 1123 } 1124 1125 @Override 1126 public void process(ComponentDefinitionRegistry componentDefinitionRegistry) { 1127 CamelContextFactoryBean ccfb = (CamelContextFactoryBean) blueprintContainer.getComponentInstance(".camelBlueprint.factory." + camelContextName); 1128 CamelContext camelContext = ccfb.getContext(); 1129 1130 Set<String> components = new HashSet<>(); 1131 Set<String> languages = new HashSet<>(); 1132 Set<String> dataformats = new HashSet<>(); 1133 1134 // regular camel routes 1135 for (RouteDefinition rd : camelContext.getExtension(Model.class).getRouteDefinitions()) { 1136 findInputComponents(rd.getInput(), components, languages, dataformats); 1137 findOutputComponents(rd.getOutputs(), components, languages, dataformats); 1138 } 1139 1140 // rest services can have embedded routes or a singular to 1141 for (RestDefinition rd : camelContext.getExtension(Model.class).getRestDefinitions()) { 1142 for (VerbDefinition vd : rd.getVerbs()) { 1143 ToDefinition to = vd.getTo(); 1144 if (to != null) { 1145 findUriComponent(to.getUri(), components); 1146 } 1147 } 1148 } 1149 1150 if (ccfb.getRestConfiguration() != null) { 1151 // rest configuration may refer to a component to use 1152 String component = ccfb.getRestConfiguration().getComponent(); 1153 if (component != null) { 1154 components.add(component); 1155 } 1156 component = ccfb.getRestConfiguration().getApiComponent(); 1157 if (component != null) { 1158 components.add(component); 1159 } 1160 1161 // check what data formats are used in binding mode 1162 RestBindingMode mode = ccfb.getRestConfiguration().getBindingMode(); 1163 String json = ccfb.getRestConfiguration().getJsonDataFormat(); 1164 if (json == null && mode != null) { 1165 if (RestBindingMode.json.equals(mode) || RestBindingMode.json_xml.equals(mode)) { 1166 // jackson is the default json data format 1167 json = "jackson"; 1168 } 1169 } 1170 if (json != null) { 1171 dataformats.add(json); 1172 } 1173 String xml = ccfb.getRestConfiguration().getXmlDataFormat(); 1174 if (xml == null && mode != null) { 1175 if (RestBindingMode.xml.equals(mode) || RestBindingMode.json_xml.equals(mode)) { 1176 // jaxb is the default xml data format 1177 dataformats.add("jaxb"); 1178 } 1179 } 1180 if (xml != null) { 1181 dataformats.add(xml); 1182 } 1183 } 1184 1185 // We can only add service references to resolvers, but we can't make the factory depends on those 1186 // because the factory has already been instantiated 1187 try { 1188 for (String component : components) { 1189 if (camelContext.getComponent(component, false) == null) { 1190 // component not already in camel-context so resolve an OSGi reference to it 1191 getComponentResolverReference(context, component); 1192 } else { 1193 LOG.debug("Not creating a service reference for component {} because a component already exists in the Camel Context", component); 1194 } 1195 } 1196 for (String language : languages) { 1197 getLanguageResolverReference(context, language); 1198 } 1199 for (String dataformat : dataformats) { 1200 getDataformatResolverReference(context, dataformat); 1201 } 1202 } catch (UnsupportedOperationException e) { 1203 LOG.warn("Unable to add dependencies to Camel components OSGi services. " 1204 + "The Apache Aries blueprint implementation used is too old and the blueprint bundle cannot see the org.apache.camel.spi package."); 1205 components.clear(); 1206 languages.clear(); 1207 dataformats.clear(); 1208 } 1209 1210 } 1211 1212 private void findInputComponents(FromDefinition from, Set<String> components, Set<String> languages, Set<String> dataformats) { 1213 if (from != null) { 1214 findUriComponent(from.getUri(), components); 1215 findSchedulerUriComponent(from.getUri(), components); 1216 } 1217 } 1218 1219 @SuppressWarnings({"rawtypes"}) 1220 private void findOutputComponents(List<ProcessorDefinition<?>> defs, Set<String> components, Set<String> languages, Set<String> dataformats) { 1221 if (defs != null) { 1222 for (ProcessorDefinition<?> def : defs) { 1223 if (def instanceof SendDefinition) { 1224 findUriComponent(((SendDefinition) def).getUri(), components); 1225 } 1226 if (def instanceof MarshalDefinition) { 1227 findDataFormat(((MarshalDefinition) def).getDataFormatType(), dataformats); 1228 } 1229 if (def instanceof UnmarshalDefinition) { 1230 findDataFormat(((UnmarshalDefinition) def).getDataFormatType(), dataformats); 1231 } 1232 if (def instanceof ExpressionNode) { 1233 findLanguage(((ExpressionNode) def).getExpression(), languages); 1234 } 1235 if (def instanceof ResequenceDefinition) { 1236 findLanguage(((ResequenceDefinition) def).getExpression(), languages); 1237 } 1238 if (def instanceof AggregateDefinition) { 1239 findLanguage(((AggregateDefinition) def).getExpression(), languages); 1240 findLanguage(((AggregateDefinition) def).getCorrelationExpression(), languages); 1241 findLanguage(((AggregateDefinition) def).getCompletionPredicate(), languages); 1242 findLanguage(((AggregateDefinition) def).getCompletionTimeoutExpression(), languages); 1243 findLanguage(((AggregateDefinition) def).getCompletionSizeExpression(), languages); 1244 } 1245 if (def instanceof CatchDefinition) { 1246 CatchDefinition doCatch = (CatchDefinition) def; 1247 if (doCatch.getOnWhen() != null) { 1248 findLanguage(doCatch.getOnWhen().getExpression(), languages); 1249 } 1250 } 1251 if (def instanceof OnExceptionDefinition) { 1252 findLanguage(((OnExceptionDefinition) def).getRetryWhile(), languages); 1253 findLanguage(((OnExceptionDefinition) def).getHandled(), languages); 1254 findLanguage(((OnExceptionDefinition) def).getContinued(), languages); 1255 } 1256 if (def instanceof SortDefinition) { 1257 findLanguage(((SortDefinition) def).getExpression(), languages); 1258 } 1259 findOutputComponents(def.getOutputs(), components, languages, dataformats); 1260 } 1261 } 1262 } 1263 1264 private void findLanguage(ExpressionDefinition expression, Set<String> languages) { 1265 if (expression != null) { 1266 String lang = expression.getLanguage(); 1267 if (lang != null && lang.length() > 0) { 1268 languages.add(lang); 1269 } 1270 } 1271 } 1272 1273 private void findLanguage(ExpressionSubElementDefinition expression, Set<String> languages) { 1274 if (expression != null) { 1275 findLanguage(expression.getExpressionType(), languages); 1276 } 1277 } 1278 1279 private void findDataFormat(DataFormatDefinition dfd, Set<String> dataformats) { 1280 if (dfd != null && dfd.getDataFormatName() != null) { 1281 dataformats.add(dfd.getDataFormatName()); 1282 } 1283 } 1284 1285 private void findUriComponent(String uri, Set<String> components) { 1286 // if the uri is a placeholder then skip it 1287 if (uri == null || uri.startsWith(PropertiesComponent.PREFIX_TOKEN)) { 1288 return; 1289 } 1290 1291 // validate uri here up-front so a meaningful error can be logged for blueprint 1292 // it will also speed up tests in case of failure 1293 if (!validateUri(uri)) { 1294 return; 1295 } 1296 1297 String splitURI[] = StringHelper.splitOnCharacter(uri, ":", 2); 1298 if (splitURI[1] != null) { 1299 String scheme = splitURI[0]; 1300 components.add(scheme); 1301 } 1302 } 1303 1304 private void findSchedulerUriComponent(String uri, Set<String> components) { 1305 1306 // the input may use a scheduler which can be quartz or spring 1307 if (uri != null) { 1308 try { 1309 URI u = new URI(uri); 1310 Map<String, Object> parameters = URISupport.parseParameters(u); 1311 Object value = parameters.get("scheduler"); 1312 if (value != null) { 1313 // the scheduler can be quartz or spring based, so add reference to camel component 1314 // from these components os blueprint knows about the requirement 1315 String name = value.toString(); 1316 if ("quartz".equals(name)) { 1317 components.add("quartz"); 1318 } else if ("spring".equals(name)) { 1319 components.add("spring-event"); 1320 } 1321 } 1322 } catch (URISyntaxException e) { 1323 // ignore as uri should be already validated at findUriComponent method 1324 } 1325 } 1326 } 1327 1328 private static boolean validateUri(String uri) { 1329 try { 1330 // the same validation as done in DefaultCamelContext#normalizeEndpointUri(String) 1331 URISupport.normalizeUri(uri); 1332 } catch (URISyntaxException | UnsupportedEncodingException e) { 1333 LOG.error("Endpoint URI '" + uri + "' is not valid due to: " + e.getMessage(), e); 1334 return false; 1335 } 1336 return true; 1337 } 1338 } 1339 1340}