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