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