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