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