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