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