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