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