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