001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.blueprint.handler;
018
019import java.lang.reflect.Field;
020import java.lang.reflect.Method;
021import java.lang.reflect.Modifier;
022import java.net.URI;
023import java.net.URISyntaxException;
024import java.net.URL;
025import java.util.Arrays;
026import java.util.HashSet;
027import java.util.List;
028import java.util.Map;
029import java.util.Set;
030import java.util.concurrent.Callable;
031import javax.xml.bind.Binder;
032import javax.xml.bind.JAXBContext;
033import javax.xml.bind.JAXBException;
034
035import org.w3c.dom.Document;
036import org.w3c.dom.Element;
037import org.w3c.dom.NamedNodeMap;
038import org.w3c.dom.Node;
039import org.w3c.dom.NodeList;
040
041import org.apache.aries.blueprint.BeanProcessor;
042import org.apache.aries.blueprint.ComponentDefinitionRegistry;
043import org.apache.aries.blueprint.ComponentDefinitionRegistryProcessor;
044import org.apache.aries.blueprint.NamespaceHandler;
045import org.apache.aries.blueprint.ParserContext;
046import org.apache.aries.blueprint.PassThroughMetadata;
047import org.apache.aries.blueprint.mutable.MutableBeanMetadata;
048import org.apache.aries.blueprint.mutable.MutablePassThroughMetadata;
049import org.apache.aries.blueprint.mutable.MutableRefMetadata;
050import org.apache.aries.blueprint.mutable.MutableReferenceMetadata;
051import org.apache.camel.BeanInject;
052import org.apache.camel.CamelContext;
053import org.apache.camel.Endpoint;
054import org.apache.camel.EndpointInject;
055import org.apache.camel.Produce;
056import org.apache.camel.PropertyInject;
057import org.apache.camel.blueprint.BlueprintCamelContext;
058import org.apache.camel.blueprint.BlueprintModelJAXBContextFactory;
059import org.apache.camel.blueprint.CamelContextFactoryBean;
060import org.apache.camel.blueprint.CamelEndpointFactoryBean;
061import org.apache.camel.blueprint.CamelRestContextFactoryBean;
062import org.apache.camel.blueprint.CamelRouteContextFactoryBean;
063import org.apache.camel.builder.xml.Namespaces;
064import org.apache.camel.component.properties.PropertiesComponent;
065import org.apache.camel.core.xml.AbstractCamelFactoryBean;
066import org.apache.camel.impl.CamelPostProcessorHelper;
067import org.apache.camel.impl.DefaultCamelContextNameStrategy;
068import org.apache.camel.model.AggregateDefinition;
069import org.apache.camel.model.CatchDefinition;
070import org.apache.camel.model.DataFormatDefinition;
071import org.apache.camel.model.ExpressionNode;
072import org.apache.camel.model.ExpressionSubElementDefinition;
073import org.apache.camel.model.FromDefinition;
074import org.apache.camel.model.MarshalDefinition;
075import org.apache.camel.model.OnExceptionDefinition;
076import org.apache.camel.model.ProcessorDefinition;
077import org.apache.camel.model.ResequenceDefinition;
078import org.apache.camel.model.RouteDefinition;
079import org.apache.camel.model.SendDefinition;
080import org.apache.camel.model.SortDefinition;
081import org.apache.camel.model.ToDefinition;
082import org.apache.camel.model.ToDynamicDefinition;
083import org.apache.camel.model.UnmarshalDefinition;
084import org.apache.camel.model.WireTapDefinition;
085import org.apache.camel.model.language.ExpressionDefinition;
086import org.apache.camel.model.rest.RestBindingMode;
087import org.apache.camel.model.rest.RestDefinition;
088import org.apache.camel.model.rest.VerbDefinition;
089import org.apache.camel.spi.CamelContextNameStrategy;
090import org.apache.camel.spi.ComponentResolver;
091import org.apache.camel.spi.DataFormatResolver;
092import org.apache.camel.spi.LanguageResolver;
093import org.apache.camel.spi.NamespaceAware;
094import org.apache.camel.util.ObjectHelper;
095import org.apache.camel.util.URISupport;
096import org.apache.camel.util.blueprint.KeyStoreParametersFactoryBean;
097import org.apache.camel.util.blueprint.SSLContextParametersFactoryBean;
098import org.apache.camel.util.blueprint.SecureRandomParametersFactoryBean;
099import org.apache.camel.util.jsse.KeyStoreParameters;
100import org.apache.camel.util.jsse.SSLContextParameters;
101import org.apache.camel.util.jsse.SecureRandomParameters;
102import org.osgi.framework.Bundle;
103import org.osgi.service.blueprint.container.BlueprintContainer;
104import org.osgi.service.blueprint.container.ComponentDefinitionException;
105import org.osgi.service.blueprint.reflect.BeanMetadata;
106import org.osgi.service.blueprint.reflect.ComponentMetadata;
107import org.osgi.service.blueprint.reflect.Metadata;
108import org.osgi.service.blueprint.reflect.RefMetadata;
109import org.slf4j.Logger;
110import org.slf4j.LoggerFactory;
111
112import static org.osgi.service.blueprint.reflect.ComponentMetadata.ACTIVATION_LAZY;
113import static org.osgi.service.blueprint.reflect.ServiceReferenceMetadata.AVAILABILITY_MANDATORY;
114import static org.osgi.service.blueprint.reflect.ServiceReferenceMetadata.AVAILABILITY_OPTIONAL;
115
116/**
117 * Camel {@link NamespaceHandler} to parse the Camel related namespaces.
118 */
119public class CamelNamespaceHandler implements NamespaceHandler {
120
121    public static final String BLUEPRINT_NS = "http://camel.apache.org/schema/blueprint";
122    public static final String SPRING_NS = "http://camel.apache.org/schema/spring";
123
124    private static final String CAMEL_CONTEXT = "camelContext";
125    private static final String ROUTE_CONTEXT = "routeContext";
126    private static final String REST_CONTEXT = "restContext";
127    private static final String ENDPOINT = "endpoint";
128    private static final String KEY_STORE_PARAMETERS = "keyStoreParameters";
129    private static final String SECURE_RANDOM_PARAMETERS = "secureRandomParameters";
130    private static final String SSL_CONTEXT_PARAMETERS = "sslContextParameters";
131
132    private static final Logger LOG = LoggerFactory.getLogger(CamelNamespaceHandler.class);
133
134    private JAXBContext jaxbContext;
135
136    /**
137     * Prepares the nodes before parsing.
138     */
139    public static void doBeforeParse(Node node, String fromNamespace, String toNamespace) {
140        if (node.getNodeType() == Node.ELEMENT_NODE) {
141            Document doc = node.getOwnerDocument();
142            if (node.getNamespaceURI().equals(fromNamespace)) {
143                doc.renameNode(node, toNamespace, node.getLocalName());
144            }
145
146            // remove whitespace noise from uri, xxxUri attributes, eg new lines, and tabs etc, which allows end users to format
147            // their Camel routes in more human readable format, but at runtime those attributes must be trimmed
148            // the parser removes most of the noise, but keeps double spaces in the attribute values
149            NamedNodeMap map = node.getAttributes();
150            for (int i = 0; i < map.getLength(); i++) {
151                Node att = map.item(i);
152                if (att.getNodeName().equals("uri") || att.getNodeName().endsWith("Uri")) {
153                    final String value = att.getNodeValue();
154                    String before = ObjectHelper.before(value, "?");
155                    String after = ObjectHelper.after(value, "?");
156
157                    if (before != null && after != null) {
158                        // remove all double spaces in the uri parameters
159                        String changed = after.replaceAll("\\s{2,}", "");
160                        if (!after.equals(changed)) {
161                            String newAtr = before.trim() + "?" + changed.trim();
162                            LOG.debug("Removed whitespace noise from attribute {} -> {}", value, newAtr);
163                            att.setNodeValue(newAtr);
164                        }
165                    }
166                }
167            }
168        }
169        NodeList list = node.getChildNodes();
170        for (int i = 0; i < list.getLength(); ++i) {
171            doBeforeParse(list.item(i), fromNamespace, toNamespace);
172        }
173    }
174
175    public URL getSchemaLocation(String namespace) {
176        return getClass().getClassLoader().getResource("camel-blueprint.xsd");
177    }
178
179    @SuppressWarnings({"unchecked", "rawtypes"})
180    public Set<Class> getManagedClasses() {
181        return new HashSet<Class>(Arrays.asList(BlueprintCamelContext.class));
182    }
183
184    public Metadata parse(Element element, ParserContext context) {
185        LOG.trace("Parsing element {}", element);
186
187        try {
188            // as the camel-core model namespace is Spring we need to rename from blueprint to spring
189            doBeforeParse(element, BLUEPRINT_NS, SPRING_NS);
190
191            if (element.getLocalName().equals(CAMEL_CONTEXT)) {
192                return parseCamelContextNode(element, context);
193            }
194            if (element.getLocalName().equals(ROUTE_CONTEXT)) {
195                return parseRouteContextNode(element, context);
196            }
197            if (element.getLocalName().equals(REST_CONTEXT)) {
198                return parseRestContextNode(element, context);
199            }
200            if (element.getLocalName().equals(ENDPOINT)) {
201                return parseEndpointNode(element, context);
202            }
203            if (element.getLocalName().equals(KEY_STORE_PARAMETERS)) {
204                return parseKeyStoreParametersNode(element, context);
205            }
206            if (element.getLocalName().equals(SECURE_RANDOM_PARAMETERS)) {
207                return parseSecureRandomParametersNode(element, context);
208            }
209            if (element.getLocalName().equals(SSL_CONTEXT_PARAMETERS)) {
210                return parseSSLContextParametersNode(element, context);
211            }
212        } finally {
213            // make sure to rename back so we leave the DOM as-is
214            doBeforeParse(element, SPRING_NS, BLUEPRINT_NS);
215        }
216
217        return null;
218    }
219
220    private Metadata parseCamelContextNode(Element element, ParserContext context) {
221        LOG.trace("Parsing CamelContext {}", element);
222        // Find the id, generate one if needed
223        String contextId = element.getAttribute("id");
224        boolean implicitId = false;
225
226        // let's avoid folks having to explicitly give an ID to a camel context
227        if (ObjectHelper.isEmpty(contextId)) {
228            // if no explicit id was set then use a default auto generated name
229            CamelContextNameStrategy strategy = new DefaultCamelContextNameStrategy();
230            contextId = strategy.getName();
231            element.setAttributeNS(null, "id", contextId);
232            implicitId = true;
233        }
234
235        // now let's parse the routes with JAXB
236        Binder<Node> binder;
237        try {
238            binder = getJaxbContext().createBinder();
239        } catch (JAXBException e) {
240            throw new ComponentDefinitionException("Failed to create the JAXB binder : " + e, e);
241        }
242        Object value = parseUsingJaxb(element, context, binder);
243        if (!(value instanceof CamelContextFactoryBean)) {
244            throw new ComponentDefinitionException("Expected an instance of " + CamelContextFactoryBean.class);
245        }
246
247        CamelContextFactoryBean ccfb = (CamelContextFactoryBean) value;
248        ccfb.setImplicitId(implicitId);
249
250        // The properties component is always used / created by the CamelContextFactoryBean
251        // so we need to ensure that the resolver is ready to use
252        ComponentMetadata propertiesComponentResolver = getComponentResolverReference(context, "properties");
253
254        MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class);
255        factory.setId(".camelBlueprint.passThrough." + contextId);
256        factory.setObject(new PassThroughCallable<Object>(value));
257
258        MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class);
259        factory2.setId(".camelBlueprint.factory." + contextId);
260        factory2.setFactoryComponent(factory);
261        factory2.setFactoryMethod("call");
262        factory2.setInitMethod("afterPropertiesSet");
263        factory2.setDestroyMethod("destroy");
264        factory2.addProperty("blueprintContainer", createRef(context, "blueprintContainer"));
265        factory2.addProperty("bundleContext", createRef(context, "blueprintBundleContext"));
266        factory2.addDependsOn(propertiesComponentResolver.getId());
267        // We need to add other components which the camel context dependsOn
268        if (ObjectHelper.isNotEmpty(ccfb.getDependsOn())) {
269            factory2.setDependsOn(Arrays.asList(ccfb.getDependsOn().split(" |,")));
270        }
271        context.getComponentDefinitionRegistry().registerComponentDefinition(factory2);
272
273        MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class);
274        ctx.setId(contextId);
275        ctx.setRuntimeClass(BlueprintCamelContext.class);
276        ctx.setFactoryComponent(factory2);
277        ctx.setFactoryMethod("getContext");
278        ctx.setInitMethod("init");
279        ctx.setDestroyMethod("destroy");
280
281        // Register factory beans
282        registerBeans(context, contextId, ccfb.getThreadPools());
283        registerBeans(context, contextId, ccfb.getEndpoints());
284        registerBeans(context, contextId, ccfb.getRedeliveryPolicies());
285        registerBeans(context, contextId, ccfb.getBeans());
286
287        // Register processors
288        MutablePassThroughMetadata beanProcessorFactory = context.createMetadata(MutablePassThroughMetadata.class);
289        beanProcessorFactory.setId(".camelBlueprint.processor.bean.passThrough." + contextId);
290        beanProcessorFactory.setObject(new PassThroughCallable<Object>(new CamelInjector(contextId)));
291
292        MutableBeanMetadata beanProcessor = context.createMetadata(MutableBeanMetadata.class);
293        beanProcessor.setId(".camelBlueprint.processor.bean." + contextId);
294        beanProcessor.setRuntimeClass(CamelInjector.class);
295        beanProcessor.setFactoryComponent(beanProcessorFactory);
296        beanProcessor.setFactoryMethod("call");
297        beanProcessor.setProcessor(true);
298        beanProcessor.addProperty("blueprintContainer", createRef(context, "blueprintContainer"));
299        context.getComponentDefinitionRegistry().registerComponentDefinition(beanProcessor);
300
301        MutablePassThroughMetadata regProcessorFactory = context.createMetadata(MutablePassThroughMetadata.class);
302        regProcessorFactory.setId(".camelBlueprint.processor.registry.passThrough." + contextId);
303        regProcessorFactory.setObject(new PassThroughCallable<Object>(new CamelDependenciesFinder(contextId, context)));
304
305        MutableBeanMetadata regProcessor = context.createMetadata(MutableBeanMetadata.class);
306        regProcessor.setId(".camelBlueprint.processor.registry." + contextId);
307        regProcessor.setRuntimeClass(CamelDependenciesFinder.class);
308        regProcessor.setFactoryComponent(regProcessorFactory);
309        regProcessor.setFactoryMethod("call");
310        regProcessor.setProcessor(true);
311        regProcessor.addDependsOn(".camelBlueprint.processor.bean." + contextId);
312        regProcessor.addProperty("blueprintContainer", createRef(context, "blueprintContainer"));
313        context.getComponentDefinitionRegistry().registerComponentDefinition(regProcessor);
314
315        // lets inject the namespaces into any namespace aware POJOs
316        injectNamespaces(element, binder);
317
318        LOG.trace("Parsing CamelContext done, returning {}", ctx);
319        return ctx;
320    }
321
322    protected void injectNamespaces(Element element, Binder<Node> binder) {
323        NodeList list = element.getChildNodes();
324        Namespaces namespaces = null;
325        int size = list.getLength();
326        for (int i = 0; i < size; i++) {
327            Node child = list.item(i);
328            if (child instanceof Element) {
329                Element childElement = (Element) child;
330                Object object = binder.getJAXBNode(child);
331                if (object instanceof NamespaceAware) {
332                    NamespaceAware namespaceAware = (NamespaceAware) object;
333                    if (namespaces == null) {
334                        namespaces = new Namespaces(element);
335                    }
336                    namespaces.configure(namespaceAware);
337                }
338                injectNamespaces(childElement, binder);
339            }
340        }
341    }
342
343    private Metadata parseRouteContextNode(Element element, ParserContext context) {
344        LOG.trace("Parsing RouteContext {}", element);
345        // now parse the routes with JAXB
346        Binder<Node> binder;
347        try {
348            binder = getJaxbContext().createBinder();
349        } catch (JAXBException e) {
350
351            throw new ComponentDefinitionException("Failed to create the JAXB binder : " + e, e);
352        }
353        Object value = parseUsingJaxb(element, context, binder);
354        if (!(value instanceof CamelRouteContextFactoryBean)) {
355            throw new ComponentDefinitionException("Expected an instance of " + CamelRouteContextFactoryBean.class);
356        }
357
358        CamelRouteContextFactoryBean rcfb = (CamelRouteContextFactoryBean) value;
359        String id = rcfb.getId();
360
361        MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class);
362        factory.setId(".camelBlueprint.passThrough." + id);
363        factory.setObject(new PassThroughCallable<Object>(rcfb));
364
365        MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class);
366        factory2.setId(".camelBlueprint.factory." + id);
367        factory2.setFactoryComponent(factory);
368        factory2.setFactoryMethod("call");
369
370        MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class);
371        ctx.setId(id);
372        ctx.setRuntimeClass(List.class);
373        ctx.setFactoryComponent(factory2);
374        ctx.setFactoryMethod("getRoutes");
375        // must be lazy as we want CamelContext to be activated first
376        ctx.setActivation(ACTIVATION_LAZY);
377
378        // lets inject the namespaces into any namespace aware POJOs
379        injectNamespaces(element, binder);
380
381        LOG.trace("Parsing RouteContext done, returning {}", element, ctx);
382        return ctx;
383    }
384
385    private Metadata parseRestContextNode(Element element, ParserContext context) {
386        LOG.trace("Parsing RestContext {}", element);
387        // now parse the rests with JAXB
388        Binder<Node> binder;
389        try {
390            binder = getJaxbContext().createBinder();
391        } catch (JAXBException e) {
392            throw new ComponentDefinitionException("Failed to create the JAXB binder : " + e, e);
393        }
394        Object value = parseUsingJaxb(element, context, binder);
395        if (!(value instanceof CamelRestContextFactoryBean)) {
396            throw new ComponentDefinitionException("Expected an instance of " + CamelRestContextFactoryBean.class);
397        }
398
399        CamelRestContextFactoryBean rcfb = (CamelRestContextFactoryBean) value;
400        String id = rcfb.getId();
401
402        MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class);
403        factory.setId(".camelBlueprint.passThrough." + id);
404        factory.setObject(new PassThroughCallable<Object>(rcfb));
405
406        MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class);
407        factory2.setId(".camelBlueprint.factory." + id);
408        factory2.setFactoryComponent(factory);
409        factory2.setFactoryMethod("call");
410
411        MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class);
412        ctx.setId(id);
413        ctx.setRuntimeClass(List.class);
414        ctx.setFactoryComponent(factory2);
415        ctx.setFactoryMethod("getRests");
416        // must be lazy as we want CamelContext to be activated first
417        ctx.setActivation(ACTIVATION_LAZY);
418
419        // lets inject the namespaces into any namespace aware POJOs
420        injectNamespaces(element, binder);
421
422        LOG.trace("Parsing RestContext done, returning {}", element, ctx);
423        return ctx;
424    }
425
426    private Metadata parseEndpointNode(Element element, ParserContext context) {
427        LOG.trace("Parsing Endpoint {}", element);
428        // now parse the rests with JAXB
429        Binder<Node> binder;
430        try {
431            binder = getJaxbContext().createBinder();
432        } catch (JAXBException e) {
433            throw new ComponentDefinitionException("Failed to create the JAXB binder : " + e, e);
434        }
435        Object value = parseUsingJaxb(element, context, binder);
436        if (!(value instanceof CamelEndpointFactoryBean)) {
437            throw new ComponentDefinitionException("Expected an instance of " + CamelEndpointFactoryBean.class);
438        }
439
440        CamelEndpointFactoryBean rcfb = (CamelEndpointFactoryBean) value;
441        String id = rcfb.getId();
442
443        MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class);
444        factory.setId(".camelBlueprint.passThrough." + id);
445        factory.setObject(new PassThroughCallable<Object>(rcfb));
446
447        MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class);
448        factory2.setId(".camelBlueprint.factory." + id);
449        factory2.setFactoryComponent(factory);
450        factory2.setFactoryMethod("call");
451        factory2.setInitMethod("afterPropertiesSet");
452        factory2.setDestroyMethod("destroy");
453        factory2.addProperty("blueprintContainer", createRef(context, "blueprintContainer"));
454
455        MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class);
456        ctx.setId(id);
457        ctx.setRuntimeClass(Endpoint.class);
458        ctx.setFactoryComponent(factory2);
459        ctx.setFactoryMethod("getObject");
460        // must be lazy as we want CamelContext to be activated first
461        ctx.setActivation(ACTIVATION_LAZY);
462
463        LOG.trace("Parsing endpoint done, returning {}", element, ctx);
464        return ctx;
465    }
466
467    private Metadata parseKeyStoreParametersNode(Element element, ParserContext context) {
468        LOG.trace("Parsing KeyStoreParameters {}", element);
469        // now parse the key store parameters with JAXB
470        Binder<Node> binder;
471        try {
472            binder = getJaxbContext().createBinder();
473        } catch (JAXBException e) {
474            throw new ComponentDefinitionException("Failed to create the JAXB binder : " + e, e);
475        }
476        Object value = parseUsingJaxb(element, context, binder);
477        if (!(value instanceof KeyStoreParametersFactoryBean)) {
478            throw new ComponentDefinitionException("Expected an instance of " + KeyStoreParametersFactoryBean.class);
479        }
480
481        KeyStoreParametersFactoryBean kspfb = (KeyStoreParametersFactoryBean) value;
482        String id = kspfb.getId();
483
484        MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class);
485        factory.setId(".camelBlueprint.passThrough." + id);
486        factory.setObject(new PassThroughCallable<Object>(kspfb));
487
488        MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class);
489        factory2.setId(".camelBlueprint.factory." + id);
490        factory2.setFactoryComponent(factory);
491        factory2.setFactoryMethod("call");
492        factory2.setInitMethod("afterPropertiesSet");
493        factory2.setDestroyMethod("destroy");
494        factory2.addProperty("blueprintContainer", createRef(context, "blueprintContainer"));
495
496        MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class);
497        ctx.setId(id);
498        ctx.setRuntimeClass(KeyStoreParameters.class);
499        ctx.setFactoryComponent(factory2);
500        ctx.setFactoryMethod("getObject");
501        // must be lazy as we want CamelContext to be activated first
502        ctx.setActivation(ACTIVATION_LAZY);
503
504        LOG.trace("Parsing KeyStoreParameters done, returning {}", ctx);
505        return ctx;
506    }
507
508    private Metadata parseSecureRandomParametersNode(Element element, ParserContext context) {
509        LOG.trace("Parsing SecureRandomParameters {}", element);
510        // now parse the key store parameters with JAXB
511        Binder<Node> binder;
512        try {
513            binder = getJaxbContext().createBinder();
514        } catch (JAXBException e) {
515            throw new ComponentDefinitionException("Failed to create the JAXB binder : " + e, e);
516        }
517        Object value = parseUsingJaxb(element, context, binder);
518        if (!(value instanceof SecureRandomParametersFactoryBean)) {
519            throw new ComponentDefinitionException("Expected an instance of " + SecureRandomParametersFactoryBean.class);
520        }
521
522        SecureRandomParametersFactoryBean srfb = (SecureRandomParametersFactoryBean) value;
523        String id = srfb.getId();
524
525        MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class);
526        factory.setId(".camelBlueprint.passThrough." + id);
527        factory.setObject(new PassThroughCallable<Object>(srfb));
528
529        MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class);
530        factory2.setId(".camelBlueprint.factory." + id);
531        factory2.setFactoryComponent(factory);
532        factory2.setFactoryMethod("call");
533        factory2.setInitMethod("afterPropertiesSet");
534        factory2.setDestroyMethod("destroy");
535        factory2.addProperty("blueprintContainer", createRef(context, "blueprintContainer"));
536
537        MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class);
538        ctx.setId(id);
539        ctx.setRuntimeClass(SecureRandomParameters.class);
540        ctx.setFactoryComponent(factory2);
541        ctx.setFactoryMethod("getObject");
542        // must be lazy as we want CamelContext to be activated first
543        ctx.setActivation(ACTIVATION_LAZY);
544
545        LOG.trace("Parsing SecureRandomParameters done, returning {}", ctx);
546        return ctx;
547    }
548
549    private Metadata parseSSLContextParametersNode(Element element, ParserContext context) {
550        LOG.trace("Parsing SSLContextParameters {}", element);
551        // now parse the key store parameters with JAXB
552        Binder<Node> binder;
553        try {
554            binder = getJaxbContext().createBinder();
555        } catch (JAXBException e) {
556            throw new ComponentDefinitionException("Failed to create the JAXB binder : " + e, e);
557        }
558        Object value = parseUsingJaxb(element, context, binder);
559        if (!(value instanceof SSLContextParametersFactoryBean)) {
560            throw new ComponentDefinitionException("Expected an instance of " + SSLContextParametersFactoryBean.class);
561        }
562
563        SSLContextParametersFactoryBean scpfb = (SSLContextParametersFactoryBean) value;
564        String id = scpfb.getId();
565
566        MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class);
567        factory.setId(".camelBlueprint.passThrough." + id);
568        factory.setObject(new PassThroughCallable<Object>(scpfb));
569
570        MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class);
571        factory2.setId(".camelBlueprint.factory." + id);
572        factory2.setFactoryComponent(factory);
573        factory2.setFactoryMethod("call");
574        factory2.setInitMethod("afterPropertiesSet");
575        factory2.setDestroyMethod("destroy");
576        factory2.addProperty("blueprintContainer", createRef(context, "blueprintContainer"));
577
578        MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class);
579        ctx.setId(id);
580        ctx.setRuntimeClass(SSLContextParameters.class);
581        ctx.setFactoryComponent(factory2);
582        ctx.setFactoryMethod("getObject");
583        // must be lazy as we want CamelContext to be activated first
584        ctx.setActivation(ACTIVATION_LAZY);
585
586        LOG.trace("Parsing SSLContextParameters done, returning {}", ctx);
587        return ctx;
588    }
589
590    private void registerBeans(ParserContext context, String contextId, List<?> beans) {
591        if (beans != null) {
592            for (Object bean : beans) {
593                if (bean instanceof AbstractCamelFactoryBean) {
594                    registerBean(context, contextId, (AbstractCamelFactoryBean<?>) bean);
595                }
596            }
597        }
598    }
599
600    protected void registerBean(ParserContext context, String contextId, AbstractCamelFactoryBean<?> fact) {
601        String id = fact.getId();
602
603        fact.setCamelContextId(contextId);
604
605        MutablePassThroughMetadata eff = context.createMetadata(MutablePassThroughMetadata.class);
606        eff.setId(".camelBlueprint.bean.passthrough." + id);
607        eff.setObject(new PassThroughCallable<Object>(fact));
608
609        MutableBeanMetadata ef = context.createMetadata(MutableBeanMetadata.class);
610        ef.setId(".camelBlueprint.bean.factory." + id);
611        ef.setFactoryComponent(eff);
612        ef.setFactoryMethod("call");
613        ef.addProperty("blueprintContainer", createRef(context, "blueprintContainer"));
614        ef.setInitMethod("afterPropertiesSet");
615        ef.setDestroyMethod("destroy");
616
617        MutableBeanMetadata e = context.createMetadata(MutableBeanMetadata.class);
618        e.setId(id);
619        e.setRuntimeClass(fact.getObjectType());
620        e.setFactoryComponent(ef);
621        e.setFactoryMethod("getObject");
622        e.addDependsOn(".camelBlueprint.processor.bean." + contextId);
623
624        context.getComponentDefinitionRegistry().registerComponentDefinition(e);
625    }
626
627    protected BlueprintContainer getBlueprintContainer(ParserContext context) {
628        PassThroughMetadata ptm = (PassThroughMetadata) context.getComponentDefinitionRegistry().getComponentDefinition("blueprintContainer");
629        return (BlueprintContainer) ptm.getObject();
630    }
631
632    public ComponentMetadata decorate(Node node, ComponentMetadata component, ParserContext context) {
633        return null;
634    }
635
636    protected Object parseUsingJaxb(Element element, ParserContext parserContext, Binder<Node> binder) {
637        try {
638            return binder.unmarshal(element);
639        } catch (JAXBException e) {
640            throw new ComponentDefinitionException("Failed to parse JAXB element: " + e, e);
641        }
642    }
643
644    public JAXBContext getJaxbContext() throws JAXBException {
645        if (jaxbContext == null) {
646            jaxbContext = new BlueprintModelJAXBContextFactory(getClass().getClassLoader()).newJAXBContext();
647        }
648        return jaxbContext;
649    }
650
651    private RefMetadata createRef(ParserContext context, String value) {
652        MutableRefMetadata r = context.createMetadata(MutableRefMetadata.class);
653        r.setComponentId(value);
654        return r;
655    }
656
657    private static ComponentMetadata getDataformatResolverReference(ParserContext context, String dataformat) {
658        // we cannot resolve dataformat names using property placeholders at this point in time
659        if (dataformat.startsWith(PropertiesComponent.DEFAULT_PREFIX_TOKEN)) {
660            return null;
661        }
662        ComponentDefinitionRegistry componentDefinitionRegistry = context.getComponentDefinitionRegistry();
663        ComponentMetadata cm = componentDefinitionRegistry.getComponentDefinition(".camelBlueprint.dataformatResolver." + dataformat);
664        if (cm == null) {
665            MutableReferenceMetadata svc = context.createMetadata(MutableReferenceMetadata.class);
666            svc.setId(".camelBlueprint.dataformatResolver." + dataformat);
667            svc.setFilter("(dataformat=" + dataformat + ")");
668            svc.setAvailability(componentDefinitionRegistry.containsComponentDefinition(dataformat) ? AVAILABILITY_OPTIONAL : AVAILABILITY_MANDATORY);
669            try {
670                // Try to set the runtime interface (only with aries blueprint > 0.1
671                svc.getClass().getMethod("setRuntimeInterface", Class.class).invoke(svc, DataFormatResolver.class);
672            } catch (Throwable t) {
673                // Check if the bundle can see the class
674                try {
675                    PassThroughMetadata ptm = (PassThroughMetadata) componentDefinitionRegistry.getComponentDefinition("blueprintBundle");
676                    Bundle b = (Bundle) ptm.getObject();
677                    if (b.loadClass(DataFormatResolver.class.getName()) != DataFormatResolver.class) {
678                        throw new UnsupportedOperationException();
679                    }
680                    svc.setInterface(DataFormatResolver.class.getName());
681                } catch (Throwable t2) {
682                    throw new UnsupportedOperationException();
683                }
684            }
685            componentDefinitionRegistry.registerComponentDefinition(svc);
686            cm = svc;
687        }
688        return cm;
689    }
690
691    private static ComponentMetadata getLanguageResolverReference(ParserContext context, String language) {
692        // we cannot resolve language names using property placeholders at this point in time
693        if (language.startsWith(PropertiesComponent.DEFAULT_PREFIX_TOKEN)) {
694            return null;
695        }
696        ComponentDefinitionRegistry componentDefinitionRegistry = context.getComponentDefinitionRegistry();
697        ComponentMetadata cm = componentDefinitionRegistry.getComponentDefinition(".camelBlueprint.languageResolver." + language);
698        if (cm == null) {
699            MutableReferenceMetadata svc = context.createMetadata(MutableReferenceMetadata.class);
700            svc.setId(".camelBlueprint.languageResolver." + language);
701            svc.setFilter("(language=" + language + ")");
702            svc.setAvailability(componentDefinitionRegistry.containsComponentDefinition(language) ? AVAILABILITY_OPTIONAL : AVAILABILITY_MANDATORY);
703            try {
704                // Try to set the runtime interface (only with aries blueprint > 0.1
705                svc.getClass().getMethod("setRuntimeInterface", Class.class).invoke(svc, LanguageResolver.class);
706            } catch (Throwable t) {
707                // Check if the bundle can see the class
708                try {
709                    PassThroughMetadata ptm = (PassThroughMetadata) componentDefinitionRegistry.getComponentDefinition("blueprintBundle");
710                    Bundle b = (Bundle) ptm.getObject();
711                    if (b.loadClass(LanguageResolver.class.getName()) != LanguageResolver.class) {
712                        throw new UnsupportedOperationException();
713                    }
714                    svc.setInterface(LanguageResolver.class.getName());
715                } catch (Throwable t2) {
716                    throw new UnsupportedOperationException();
717                }
718            }
719            componentDefinitionRegistry.registerComponentDefinition(svc);
720            cm = svc;
721        }
722        return cm;
723    }
724
725    private static ComponentMetadata getComponentResolverReference(ParserContext context, String component) {
726        // we cannot resolve component names using property placeholders at this point in time
727        if (component.startsWith(PropertiesComponent.DEFAULT_PREFIX_TOKEN)) {
728            return null;
729        }
730        ComponentDefinitionRegistry componentDefinitionRegistry = context.getComponentDefinitionRegistry();
731        ComponentMetadata cm = componentDefinitionRegistry.getComponentDefinition(".camelBlueprint.componentResolver." + component);
732        if (cm == null) {
733            MutableReferenceMetadata svc = context.createMetadata(MutableReferenceMetadata.class);
734            svc.setId(".camelBlueprint.componentResolver." + component);
735            svc.setFilter("(component=" + component + ")");
736            svc.setAvailability(componentDefinitionRegistry.containsComponentDefinition(component) ? AVAILABILITY_OPTIONAL : AVAILABILITY_MANDATORY);
737            try {
738                // Try to set the runtime interface (only with aries blueprint > 0.1
739                svc.getClass().getMethod("setRuntimeInterface", Class.class).invoke(svc, ComponentResolver.class);
740            } catch (Throwable t) {
741                // Check if the bundle can see the class
742                try {
743                    PassThroughMetadata ptm = (PassThroughMetadata) componentDefinitionRegistry.getComponentDefinition("blueprintBundle");
744                    Bundle b = (Bundle) ptm.getObject();
745                    if (b.loadClass(ComponentResolver.class.getName()) != ComponentResolver.class) {
746                        throw new UnsupportedOperationException();
747                    }
748                    svc.setInterface(ComponentResolver.class.getName());
749                } catch (Throwable t2) {
750                    throw new UnsupportedOperationException();
751                }
752            }
753            componentDefinitionRegistry.registerComponentDefinition(svc);
754            cm = svc;
755        }
756        return cm;
757    }
758
759    public static class PassThroughCallable<T> implements Callable<T> {
760
761        private T value;
762
763        public PassThroughCallable(T value) {
764            this.value = value;
765        }
766
767        public T call() throws Exception {
768            return value;
769        }
770    }
771
772    public static class CamelInjector extends CamelPostProcessorHelper implements BeanProcessor {
773
774        private final String camelContextName;
775        private BlueprintContainer blueprintContainer;
776
777        public CamelInjector(String camelContextName) {
778            this.camelContextName = camelContextName;
779        }
780
781        public void setBlueprintContainer(BlueprintContainer blueprintContainer) {
782            this.blueprintContainer = blueprintContainer;
783        }
784
785        @Override
786        public CamelContext getCamelContext() {
787            if (blueprintContainer != null) {
788                CamelContext answer = (CamelContext) blueprintContainer.getComponentInstance(camelContextName);
789                return answer;
790            }
791            return null;
792        }
793
794        public Object beforeInit(Object bean, String beanName, BeanCreator beanCreator, BeanMetadata beanMetadata) {
795            LOG.trace("Before init of bean: {} -> {}", beanName, bean);
796            // prefer to inject later in afterInit
797            return bean;
798        }
799
800        /**
801         * A strategy method to allow implementations to perform some custom JBI
802         * based injection of the POJO
803         *
804         * @param bean the bean to be injected
805         */
806        protected void injectFields(final Object bean, final String beanName) {
807            Class<?> clazz = bean.getClass();
808            do {
809                Field[] fields = clazz.getDeclaredFields();
810                for (Field field : fields) {
811                    PropertyInject propertyInject = field.getAnnotation(PropertyInject.class);
812                    if (propertyInject != null && matchContext(propertyInject.context())) {
813                        injectFieldProperty(field, propertyInject.value(), propertyInject.defaultValue(), bean, beanName);
814                    }
815
816                    BeanInject beanInject = field.getAnnotation(BeanInject.class);
817                    if (beanInject != null && matchContext(beanInject.context())) {
818                        injectFieldBean(field, beanInject.value(), bean, beanName);
819                    }
820
821                    EndpointInject endpointInject = field.getAnnotation(EndpointInject.class);
822                    if (endpointInject != null && matchContext(endpointInject.context())) {
823                        injectField(field, endpointInject.uri(), endpointInject.ref(), endpointInject.property(), bean, beanName);
824                    }
825
826                    Produce produce = field.getAnnotation(Produce.class);
827                    if (produce != null && matchContext(produce.context())) {
828                        injectField(field, produce.uri(), produce.ref(), produce.property(), bean, beanName);
829                    }
830                }
831                clazz = clazz.getSuperclass();
832            } while (clazz != null && clazz != Object.class);
833        }
834
835        protected void injectField(Field field, String endpointUri, String endpointRef, String endpointProperty, Object bean, String beanName) {
836            setField(field, bean, getInjectionValue(field.getType(), endpointUri, endpointRef, endpointProperty, field.getName(), bean, beanName));
837        }
838
839        protected void injectFieldProperty(Field field, String propertyName, String propertyDefaultValue, Object bean, String beanName) {
840            setField(field, bean, getInjectionPropertyValue(field.getType(), propertyName, propertyDefaultValue, field.getName(), bean, beanName));
841        }
842
843        public void injectFieldBean(Field field, String name, Object bean, String beanName) {
844            setField(field, bean, getInjectionBeanValue(field.getType(), name));
845        }
846
847        protected static void setField(Field field, Object instance, Object value) {
848            try {
849                boolean oldAccessible = field.isAccessible();
850                boolean shouldSetAccessible = !Modifier.isPublic(field.getModifiers()) && !oldAccessible;
851                if (shouldSetAccessible) {
852                    field.setAccessible(true);
853                }
854                field.set(instance, value);
855                if (shouldSetAccessible) {
856                    field.setAccessible(oldAccessible);
857                }
858            } catch (IllegalArgumentException ex) {
859                throw new UnsupportedOperationException("Cannot inject value of class: " + value.getClass() + " into: " + field);
860            } catch (IllegalAccessException ex) {
861                throw new IllegalStateException("Could not access method: " + ex.getMessage());
862            }
863        }
864
865        protected void injectMethods(final Object bean, final String beanName) {
866            Class<?> clazz = bean.getClass();
867            do {
868                Method[] methods = clazz.getDeclaredMethods();
869                for (Method method : methods) {
870                    setterInjection(method, bean, beanName);
871                    consumerInjection(method, bean, beanName);
872                }
873                clazz = clazz.getSuperclass();
874            } while (clazz != null && clazz != Object.class);
875        }
876
877        protected void setterInjection(Method method, Object bean, String beanName) {
878            PropertyInject propertyInject = method.getAnnotation(PropertyInject.class);
879            if (propertyInject != null && matchContext(propertyInject.context())) {
880                setterPropertyInjection(method, propertyInject.value(), propertyInject.defaultValue(), bean, beanName);
881            }
882
883            BeanInject beanInject = method.getAnnotation(BeanInject.class);
884            if (beanInject != null && matchContext(beanInject.context())) {
885                setterBeanInjection(method, beanInject.value(), bean, beanName);
886            }
887
888            EndpointInject endpointInject = method.getAnnotation(EndpointInject.class);
889            if (endpointInject != null && matchContext(endpointInject.context())) {
890                setterInjection(method, bean, beanName, endpointInject.uri(), endpointInject.ref(), endpointInject.property());
891            }
892
893            Produce produce = method.getAnnotation(Produce.class);
894            if (produce != null && matchContext(produce.context())) {
895                setterInjection(method, bean, beanName, produce.uri(), produce.ref(), produce.property());
896            }
897        }
898
899        protected void setterPropertyInjection(Method method, String propertyValue, String propertyDefaultValue, Object bean, String beanName) {
900            Class<?>[] parameterTypes = method.getParameterTypes();
901            if (parameterTypes != null) {
902                if (parameterTypes.length != 1) {
903                    LOG.warn("Ignoring badly annotated method for injection due to incorrect number of parameters: " + method);
904                } else {
905                    String propertyName = ObjectHelper.getPropertyName(method);
906                    Object value = getInjectionPropertyValue(parameterTypes[0], propertyValue, propertyDefaultValue, propertyName, bean, beanName);
907                    ObjectHelper.invokeMethod(method, bean, value);
908                }
909            }
910        }
911
912        protected void setterBeanInjection(Method method, String name, Object bean, String beanName) {
913            Class<?>[] parameterTypes = method.getParameterTypes();
914            if (parameterTypes != null) {
915                if (parameterTypes.length != 1) {
916                    LOG.warn("Ignoring badly annotated method for injection due to incorrect number of parameters: " + method);
917                } else {
918                    Object value = getInjectionBeanValue(parameterTypes[0], name);
919                    ObjectHelper.invokeMethod(method, bean, value);
920                }
921            }
922        }
923
924        protected void setterInjection(Method method, Object bean, String beanName, String endpointUri, String endpointRef, String endpointProperty) {
925            Class<?>[] parameterTypes = method.getParameterTypes();
926            if (parameterTypes != null) {
927                if (parameterTypes.length != 1) {
928                    LOG.warn("Ignoring badly annotated method for injection due to incorrect number of parameters: " + method);
929                } else {
930                    String propertyName = ObjectHelper.getPropertyName(method);
931                    Object value = getInjectionValue(parameterTypes[0], endpointUri, endpointRef, endpointProperty, propertyName, bean, beanName);
932                    ObjectHelper.invokeMethod(method, bean, value);
933                }
934            }
935        }
936
937        public Object afterInit(Object bean, String beanName, BeanCreator beanCreator, BeanMetadata beanMetadata) {
938            LOG.trace("After init of bean: {} -> {}", beanName, bean);
939            // we cannot inject CamelContextAware beans as the CamelContext may not be ready
940            injectFields(bean, beanName);
941            injectMethods(bean, beanName);
942            return bean;
943        }
944
945        public void beforeDestroy(Object bean, String beanName) {
946        }
947
948        public void afterDestroy(Object bean, String beanName) {
949        }
950
951        @Override
952        protected boolean isSingleton(Object bean, String beanName) {
953            if (beanName != null) {
954                ComponentMetadata meta = blueprintContainer.getComponentMetadata(beanName);
955                if (meta != null && meta instanceof BeanMetadata) {
956                    String scope = ((BeanMetadata) meta).getScope();
957                    if (scope != null) {
958                        return BeanMetadata.SCOPE_SINGLETON.equals(scope);
959                    }
960                }
961            }
962            // fallback to super, which will assume singleton
963            // for beans not implementing Camel's IsSingleton interface
964            return super.isSingleton(bean, beanName);
965        }
966    }
967
968    public static class CamelDependenciesFinder implements ComponentDefinitionRegistryProcessor {
969
970        private final String camelContextName;
971        private final ParserContext context;
972        private BlueprintContainer blueprintContainer;
973
974        public CamelDependenciesFinder(String camelContextName, ParserContext context) {
975            this.camelContextName = camelContextName;
976            this.context = context;
977        }
978
979        public void setBlueprintContainer(BlueprintContainer blueprintContainer) {
980            this.blueprintContainer = blueprintContainer;
981        }
982
983        @SuppressWarnings("deprecation")
984        public void process(ComponentDefinitionRegistry componentDefinitionRegistry) {
985            CamelContextFactoryBean ccfb = (CamelContextFactoryBean) blueprintContainer.getComponentInstance(".camelBlueprint.factory." + camelContextName);
986            CamelContext camelContext = ccfb.getContext();
987
988            Set<String> components = new HashSet<String>();
989            Set<String> languages = new HashSet<String>();
990            Set<String> dataformats = new HashSet<String>();
991
992            // regular camel routes
993            for (RouteDefinition rd : camelContext.getRouteDefinitions()) {
994                findInputComponents(rd.getInputs(), components, languages, dataformats);
995                findOutputComponents(rd.getOutputs(), components, languages, dataformats);
996            }
997
998            // rest services can have embedded routes or a singular to
999            for (RestDefinition rd : camelContext.getRestDefinitions()) {
1000                for (VerbDefinition vd : rd.getVerbs()) {
1001                    Object o = vd.getToOrRoute();
1002                    if (o instanceof RouteDefinition) {
1003                        RouteDefinition route = (RouteDefinition) o;
1004                        findInputComponents(route.getInputs(), components, languages, dataformats);
1005                        findOutputComponents(route.getOutputs(), components, languages, dataformats);
1006                    } else if (o instanceof ToDefinition) {
1007                        findUriComponent(((ToDefinition) o).getUri(), components);
1008                    } else if (o instanceof ToDynamicDefinition) {
1009                        findUriComponent(((ToDynamicDefinition) o).getUri(), components);
1010                    }
1011                }
1012            }
1013
1014            if (ccfb.getRestConfiguration() != null) {
1015                // rest configuration may refer to a component to use
1016                String component = ccfb.getRestConfiguration().getComponent();
1017                if (component != null) {
1018                    components.add(component);
1019                }
1020                component = ccfb.getRestConfiguration().getApiComponent();
1021                if (component != null) {
1022                    components.add(component);
1023                }
1024
1025                // check what data formats are used in binding mode
1026                RestBindingMode mode = ccfb.getRestConfiguration().getBindingMode();
1027                String json = ccfb.getRestConfiguration().getJsonDataFormat();
1028                if (json == null && mode != null) {
1029                    if (RestBindingMode.json.equals(mode) || RestBindingMode.json_xml.equals(mode)) {
1030                        // jackson is the default json data format
1031                        json = "json-jackson";
1032                    }
1033                }
1034                if (json != null) {
1035                    dataformats.add(json);
1036                }
1037                String xml = ccfb.getRestConfiguration().getXmlDataFormat();
1038                if (xml == null && mode != null) {
1039                    if (RestBindingMode.xml.equals(mode) || RestBindingMode.json_xml.equals(mode)) {
1040                        // jaxb is the default xml data format
1041                        dataformats.add("jaxb");
1042                    }
1043                }
1044                if (xml != null) {
1045                    dataformats.add(xml);
1046                }
1047            }
1048
1049            // We can only add service references to resolvers, but we can't make the factory depends on those
1050            // because the factory has already been instantiated
1051            try {
1052                for (String component : components) {
1053                    getComponentResolverReference(context, component);
1054                }
1055                for (String language : languages) {
1056                    getLanguageResolverReference(context, language);
1057                }
1058                for (String dataformat : dataformats) {
1059                    getDataformatResolverReference(context, dataformat);
1060                }
1061            } catch (UnsupportedOperationException e) {
1062                LOG.warn("Unable to add dependencies to Camel components OSGi services. "
1063                    + "The Apache Aries blueprint implementation used is too old and the blueprint bundle cannot see the org.apache.camel.spi package.");
1064                components.clear();
1065                languages.clear();
1066                dataformats.clear();
1067            }
1068
1069        }
1070
1071        private void findInputComponents(List<FromDefinition> defs, Set<String> components, Set<String> languages, Set<String> dataformats) {
1072            if (defs != null) {
1073                for (FromDefinition def : defs) {
1074                    findUriComponent(def.getUri(), components);
1075                    findSchedulerUriComponent(def.getUri(), components);
1076                }
1077            }
1078        }
1079
1080        @SuppressWarnings({"rawtypes"})
1081        private void findOutputComponents(List<ProcessorDefinition<?>> defs, Set<String> components, Set<String> languages, Set<String> dataformats) {
1082            if (defs != null) {
1083                for (ProcessorDefinition<?> def : defs) {
1084                    if (def instanceof SendDefinition) {
1085                        findUriComponent(((SendDefinition) def).getUri(), components);
1086                    }
1087                    if (def instanceof MarshalDefinition) {
1088                        findDataFormat(((MarshalDefinition) def).getDataFormatType(), dataformats);
1089                    }
1090                    if (def instanceof UnmarshalDefinition) {
1091                        findDataFormat(((UnmarshalDefinition) def).getDataFormatType(), dataformats);
1092                    }
1093                    if (def instanceof ExpressionNode) {
1094                        findLanguage(((ExpressionNode) def).getExpression(), languages);
1095                    }
1096                    if (def instanceof ResequenceDefinition) {
1097                        findLanguage(((ResequenceDefinition) def).getExpression(), languages);
1098                    }
1099                    if (def instanceof AggregateDefinition) {
1100                        findLanguage(((AggregateDefinition) def).getExpression(), languages);
1101                        findLanguage(((AggregateDefinition) def).getCorrelationExpression(), languages);
1102                        findLanguage(((AggregateDefinition) def).getCompletionPredicate(), languages);
1103                        findLanguage(((AggregateDefinition) def).getCompletionTimeoutExpression(), languages);
1104                        findLanguage(((AggregateDefinition) def).getCompletionSizeExpression(), languages);
1105                    }
1106                    if (def instanceof CatchDefinition) {
1107                        findLanguage(((CatchDefinition) def).getHandled(), languages);
1108                    }
1109                    if (def instanceof OnExceptionDefinition) {
1110                        findLanguage(((OnExceptionDefinition) def).getRetryWhile(), languages);
1111                        findLanguage(((OnExceptionDefinition) def).getHandled(), languages);
1112                        findLanguage(((OnExceptionDefinition) def).getContinued(), languages);
1113                    }
1114                    if (def instanceof SortDefinition) {
1115                        findLanguage(((SortDefinition) def).getExpression(), languages);
1116                    }
1117                    if (def instanceof WireTapDefinition) {
1118                        findLanguage(((WireTapDefinition<?>) def).getNewExchangeExpression(), languages);
1119                    }
1120                    findOutputComponents(def.getOutputs(), components, languages, dataformats);
1121                }
1122            }
1123        }
1124
1125        private void findLanguage(ExpressionDefinition expression, Set<String> languages) {
1126            if (expression != null) {
1127                String lang = expression.getLanguage();
1128                if (lang != null && lang.length() > 0) {
1129                    languages.add(lang);
1130                }
1131            }
1132        }
1133
1134        private void findLanguage(ExpressionSubElementDefinition expression, Set<String> languages) {
1135            if (expression != null) {
1136                findLanguage(expression.getExpressionType(), languages);
1137            }
1138        }
1139
1140        private void findDataFormat(DataFormatDefinition dfd, Set<String> dataformats) {
1141            if (dfd != null && dfd.getDataFormatName() != null) {
1142                dataformats.add(dfd.getDataFormatName());
1143            }
1144        }
1145
1146        private void findUriComponent(String uri, Set<String> components) {
1147            // if the uri is a placeholder then skip it
1148            if (uri != null && uri.startsWith(PropertiesComponent.DEFAULT_PREFIX_TOKEN)) {
1149                return;
1150            }
1151
1152            if (uri != null) {
1153                String splitURI[] = ObjectHelper.splitOnCharacter(uri, ":", 2);
1154                if (splitURI[1] != null) {
1155                    String scheme = splitURI[0];
1156                    components.add(scheme);
1157                }
1158            }
1159        }
1160
1161        private void findSchedulerUriComponent(String uri, Set<String> components) {
1162
1163            // the input may use a scheduler which can be quartz or spring
1164            if (uri != null) {
1165                try {
1166                    URI u = new URI(uri);
1167                    Map<String, Object> parameters = URISupport.parseParameters(u);
1168                    Object value = parameters.get("scheduler");
1169                    if (value == null) {
1170                        value = parameters.get("consumer.scheduler");
1171                    }
1172                    if (value != null) {
1173                        // the scheduler can be quartz2 or spring based, so add reference to camel component
1174                        // from these components os blueprint knows about the requirement
1175                        String name = value.toString();
1176                        if ("quartz2".equals(name)) {
1177                            components.add("quartz2");
1178                        } else if ("spring".equals(name)) {
1179                            components.add("spring-event");
1180                        }
1181                    }
1182                } catch (URISyntaxException e) {
1183                    // ignore
1184                }
1185            }
1186        }
1187
1188    }
1189
1190}