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