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