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 */
017 package org.apache.camel.blueprint.handler;
018
019 import java.lang.reflect.Field;
020 import java.lang.reflect.Method;
021 import java.lang.reflect.Modifier;
022 import java.net.URI;
023 import java.net.URISyntaxException;
024 import java.net.URL;
025 import java.util.Arrays;
026 import java.util.HashSet;
027 import java.util.List;
028 import java.util.Map;
029 import java.util.Set;
030 import java.util.concurrent.Callable;
031 import javax.xml.bind.Binder;
032 import javax.xml.bind.JAXBContext;
033 import javax.xml.bind.JAXBException;
034
035 import org.w3c.dom.Document;
036 import org.w3c.dom.Element;
037 import org.w3c.dom.Node;
038 import org.w3c.dom.NodeList;
039
040 import org.apache.aries.blueprint.BeanProcessor;
041 import org.apache.aries.blueprint.ComponentDefinitionRegistry;
042 import org.apache.aries.blueprint.ComponentDefinitionRegistryProcessor;
043 import org.apache.aries.blueprint.NamespaceHandler;
044 import org.apache.aries.blueprint.ParserContext;
045 import org.apache.aries.blueprint.PassThroughMetadata;
046 import org.apache.aries.blueprint.mutable.MutableBeanMetadata;
047 import org.apache.aries.blueprint.mutable.MutablePassThroughMetadata;
048 import org.apache.aries.blueprint.mutable.MutableRefMetadata;
049 import org.apache.aries.blueprint.mutable.MutableReferenceMetadata;
050 import org.apache.camel.BeanInject;
051 import org.apache.camel.CamelContext;
052 import org.apache.camel.EndpointInject;
053 import org.apache.camel.Produce;
054 import org.apache.camel.PropertyInject;
055 import org.apache.camel.blueprint.BlueprintCamelContext;
056 import org.apache.camel.blueprint.CamelContextFactoryBean;
057 import org.apache.camel.blueprint.CamelRouteContextFactoryBean;
058 import org.apache.camel.builder.xml.Namespaces;
059 import org.apache.camel.core.xml.AbstractCamelContextFactoryBean;
060 import org.apache.camel.core.xml.AbstractCamelFactoryBean;
061 import org.apache.camel.impl.CamelPostProcessorHelper;
062 import org.apache.camel.impl.DefaultCamelContextNameStrategy;
063 import org.apache.camel.model.AggregateDefinition;
064 import org.apache.camel.model.CatchDefinition;
065 import org.apache.camel.model.DataFormatDefinition;
066 import org.apache.camel.model.ExpressionNode;
067 import org.apache.camel.model.ExpressionSubElementDefinition;
068 import org.apache.camel.model.FromDefinition;
069 import org.apache.camel.model.MarshalDefinition;
070 import org.apache.camel.model.OnExceptionDefinition;
071 import org.apache.camel.model.ProcessorDefinition;
072 import org.apache.camel.model.ResequenceDefinition;
073 import org.apache.camel.model.RouteDefinition;
074 import org.apache.camel.model.SendDefinition;
075 import org.apache.camel.model.SortDefinition;
076 import org.apache.camel.model.UnmarshalDefinition;
077 import org.apache.camel.model.WireTapDefinition;
078 import org.apache.camel.model.language.ExpressionDefinition;
079 import org.apache.camel.spi.CamelContextNameStrategy;
080 import org.apache.camel.spi.ComponentResolver;
081 import org.apache.camel.spi.DataFormatResolver;
082 import org.apache.camel.spi.LanguageResolver;
083 import org.apache.camel.spi.NamespaceAware;
084 import org.apache.camel.util.ObjectHelper;
085 import org.apache.camel.util.URISupport;
086 import org.apache.camel.util.blueprint.KeyStoreParametersFactoryBean;
087 import org.apache.camel.util.blueprint.SSLContextParametersFactoryBean;
088 import org.apache.camel.util.blueprint.SecureRandomParametersFactoryBean;
089 import org.apache.camel.util.jsse.KeyStoreParameters;
090 import org.apache.camel.util.jsse.SSLContextParameters;
091 import org.apache.camel.util.jsse.SecureRandomParameters;
092 import org.osgi.framework.Bundle;
093 import org.osgi.service.blueprint.container.BlueprintContainer;
094 import org.osgi.service.blueprint.container.ComponentDefinitionException;
095 import org.osgi.service.blueprint.reflect.BeanMetadata;
096 import org.osgi.service.blueprint.reflect.ComponentMetadata;
097 import org.osgi.service.blueprint.reflect.Metadata;
098 import org.osgi.service.blueprint.reflect.RefMetadata;
099 import org.slf4j.Logger;
100 import org.slf4j.LoggerFactory;
101
102 import static org.osgi.service.blueprint.reflect.ComponentMetadata.ACTIVATION_LAZY;
103 import static org.osgi.service.blueprint.reflect.ServiceReferenceMetadata.AVAILABILITY_MANDATORY;
104 import static org.osgi.service.blueprint.reflect.ServiceReferenceMetadata.AVAILABILITY_OPTIONAL;
105
106 /**
107 * Camel {@link NamespaceHandler} to parse the Camel related namespaces.
108 */
109 public 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 }