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