001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.blueprint.handler;
018
019import java.lang.reflect.Field;
020import java.lang.reflect.Method;
021import java.lang.reflect.Modifier;
022import java.net.URI;
023import java.net.URISyntaxException;
024import java.net.URL;
025import java.util.Arrays;
026import java.util.HashSet;
027import java.util.List;
028import java.util.Map;
029import java.util.Set;
030import java.util.concurrent.Callable;
031import javax.xml.bind.Binder;
032import javax.xml.bind.JAXBContext;
033import javax.xml.bind.JAXBException;
034
035import org.w3c.dom.Document;
036import org.w3c.dom.Element;
037import org.w3c.dom.Node;
038import org.w3c.dom.NodeList;
039
040import org.apache.aries.blueprint.BeanProcessor;
041import org.apache.aries.blueprint.ComponentDefinitionRegistry;
042import org.apache.aries.blueprint.ComponentDefinitionRegistryProcessor;
043import org.apache.aries.blueprint.NamespaceHandler;
044import org.apache.aries.blueprint.ParserContext;
045import org.apache.aries.blueprint.PassThroughMetadata;
046import org.apache.aries.blueprint.mutable.MutableBeanMetadata;
047import org.apache.aries.blueprint.mutable.MutablePassThroughMetadata;
048import org.apache.aries.blueprint.mutable.MutableRefMetadata;
049import org.apache.aries.blueprint.mutable.MutableReferenceMetadata;
050import org.apache.camel.BeanInject;
051import org.apache.camel.CamelContext;
052import org.apache.camel.EndpointInject;
053import org.apache.camel.Produce;
054import org.apache.camel.PropertyInject;
055import org.apache.camel.blueprint.BlueprintCamelContext;
056import org.apache.camel.blueprint.CamelContextFactoryBean;
057import org.apache.camel.blueprint.CamelRouteContextFactoryBean;
058import org.apache.camel.builder.xml.Namespaces;
059import org.apache.camel.core.xml.AbstractCamelContextFactoryBean;
060import org.apache.camel.core.xml.AbstractCamelFactoryBean;
061import org.apache.camel.impl.CamelPostProcessorHelper;
062import org.apache.camel.impl.DefaultCamelContextNameStrategy;
063import org.apache.camel.model.AggregateDefinition;
064import org.apache.camel.model.CatchDefinition;
065import org.apache.camel.model.DataFormatDefinition;
066import org.apache.camel.model.ExpressionNode;
067import org.apache.camel.model.ExpressionSubElementDefinition;
068import org.apache.camel.model.FromDefinition;
069import org.apache.camel.model.MarshalDefinition;
070import org.apache.camel.model.OnExceptionDefinition;
071import org.apache.camel.model.ProcessorDefinition;
072import org.apache.camel.model.ResequenceDefinition;
073import org.apache.camel.model.RouteDefinition;
074import org.apache.camel.model.SendDefinition;
075import org.apache.camel.model.SortDefinition;
076import org.apache.camel.model.UnmarshalDefinition;
077import org.apache.camel.model.WireTapDefinition;
078import org.apache.camel.model.language.ExpressionDefinition;
079import org.apache.camel.spi.CamelContextNameStrategy;
080import org.apache.camel.spi.ComponentResolver;
081import org.apache.camel.spi.DataFormatResolver;
082import org.apache.camel.spi.LanguageResolver;
083import org.apache.camel.spi.NamespaceAware;
084import org.apache.camel.util.ObjectHelper;
085import org.apache.camel.util.URISupport;
086import org.apache.camel.util.blueprint.KeyStoreParametersFactoryBean;
087import org.apache.camel.util.blueprint.SSLContextParametersFactoryBean;
088import org.apache.camel.util.blueprint.SecureRandomParametersFactoryBean;
089import org.apache.camel.util.jsse.KeyStoreParameters;
090import org.apache.camel.util.jsse.SSLContextParameters;
091import org.apache.camel.util.jsse.SecureRandomParameters;
092import org.osgi.framework.Bundle;
093import org.osgi.service.blueprint.container.BlueprintContainer;
094import org.osgi.service.blueprint.container.ComponentDefinitionException;
095import org.osgi.service.blueprint.reflect.BeanMetadata;
096import org.osgi.service.blueprint.reflect.ComponentMetadata;
097import org.osgi.service.blueprint.reflect.Metadata;
098import org.osgi.service.blueprint.reflect.RefMetadata;
099import org.slf4j.Logger;
100import org.slf4j.LoggerFactory;
101
102import static org.osgi.service.blueprint.reflect.ComponentMetadata.ACTIVATION_LAZY;
103import static org.osgi.service.blueprint.reflect.ServiceReferenceMetadata.AVAILABILITY_MANDATORY;
104import static org.osgi.service.blueprint.reflect.ServiceReferenceMetadata.AVAILABILITY_OPTIONAL;
105
106/**
107 * Camel {@link NamespaceHandler} to parse the Camel related namespaces.
108 */
109public class CamelNamespaceHandler implements NamespaceHandler {
110
111    public static final String BLUEPRINT_NS = "http://camel.apache.org/schema/blueprint";
112    public static final String SPRING_NS = "http://camel.apache.org/schema/spring";
113
114    private static final String CAMEL_CONTEXT = "camelContext";
115    private static final String ROUTE_CONTEXT = "routeContext";
116    private static final String KEY_STORE_PARAMETERS = "keyStoreParameters";
117    private static final String SECURE_RANDOM_PARAMETERS = "secureRandomParameters";
118    private static final String SSL_CONTEXT_PARAMETERS = "sslContextParameters";
119
120    private static final Logger LOG = LoggerFactory.getLogger(CamelNamespaceHandler.class);
121
122    private JAXBContext jaxbContext;
123
124    public static void renameNamespaceRecursive(Node node, String fromNamespace, String toNamespace) {
125        if (node.getNodeType() == Node.ELEMENT_NODE) {
126            Document doc = node.getOwnerDocument();
127            if (node.getNamespaceURI().equals(fromNamespace)) {
128                doc.renameNode(node, toNamespace, node.getLocalName());
129            }
130        }
131        NodeList list = node.getChildNodes();
132        for (int i = 0; i < list.getLength(); ++i) {
133            renameNamespaceRecursive(list.item(i), fromNamespace, toNamespace);
134        }
135    }
136
137    public URL getSchemaLocation(String namespace) {
138        return getClass().getClassLoader().getResource("camel-blueprint.xsd");
139    }
140
141    @SuppressWarnings({"unchecked", "rawtypes"})
142    public Set<Class> getManagedClasses() {
143        return new HashSet<Class>(Arrays.asList(BlueprintCamelContext.class));
144    }
145
146    public Metadata parse(Element element, ParserContext context) {
147        LOG.trace("Parsing element {}", element);
148
149        try {
150            // as the camel-core model namespace is Spring we need to rename from blueprint to spring
151            renameNamespaceRecursive(element, BLUEPRINT_NS, SPRING_NS);
152
153            if (element.getLocalName().equals(CAMEL_CONTEXT)) {
154                return parseCamelContextNode(element, context);
155            }
156            if (element.getLocalName().equals(ROUTE_CONTEXT)) {
157                return parseRouteContextNode(element, context);
158            }
159            if (element.getLocalName().equals(KEY_STORE_PARAMETERS)) {
160                return parseKeyStoreParametersNode(element, context);
161            }
162            if (element.getLocalName().equals(SECURE_RANDOM_PARAMETERS)) {
163                return parseSecureRandomParametersNode(element, context);
164            }
165            if (element.getLocalName().equals(SSL_CONTEXT_PARAMETERS)) {
166                return parseSSLContextParametersNode(element, context);
167            }
168        } finally {
169            // make sure to rename back so we leave the DOM as-is
170            renameNamespaceRecursive(element, SPRING_NS, BLUEPRINT_NS);
171        }
172
173        return null;
174    }
175
176    private Metadata parseCamelContextNode(Element element, ParserContext context) {
177        LOG.trace("Parsing CamelContext {}", element);
178        // Find the id, generate one if needed
179        String contextId = element.getAttribute("id");
180        boolean implicitId = false;
181
182        // let's avoid folks having to explicitly give an ID to a camel context
183        if (ObjectHelper.isEmpty(contextId)) {
184            // if no explicit id was set then use a default auto generated name
185            CamelContextNameStrategy strategy = new DefaultCamelContextNameStrategy();
186            contextId = strategy.getName();
187            element.setAttributeNS(null, "id", contextId);
188            implicitId = true;
189        }
190
191        // now let's parse the routes with JAXB
192        Binder<Node> binder;
193        try {
194            binder = getJaxbContext().createBinder();
195        } catch (JAXBException e) {
196            throw new ComponentDefinitionException("Failed to create the JAXB binder : " + e, e);
197        }
198        Object value = parseUsingJaxb(element, context, binder);
199        if (!(value instanceof CamelContextFactoryBean)) {
200            throw new ComponentDefinitionException("Expected an instance of " + CamelContextFactoryBean.class);
201        }
202
203        CamelContextFactoryBean ccfb = (CamelContextFactoryBean) value;
204        ccfb.setImplicitId(implicitId);
205
206        // The properties component is always used / created by the CamelContextFactoryBean
207        // so we need to ensure that the resolver is ready to use
208        ComponentMetadata propertiesComponentResolver = getComponentResolverReference(context, "properties");
209
210        MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class);
211        factory.setId(".camelBlueprint.passThrough." + contextId);
212        factory.setObject(new PassThroughCallable<Object>(value));
213
214        MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class);
215        factory2.setId(".camelBlueprint.factory." + contextId);
216        factory2.setFactoryComponent(factory);
217        factory2.setFactoryMethod("call");
218        factory2.setInitMethod("afterPropertiesSet");
219        factory2.setDestroyMethod("destroy");
220        factory2.addProperty("blueprintContainer", createRef(context, "blueprintContainer"));
221        factory2.addProperty("bundleContext", createRef(context, "blueprintBundleContext"));
222        factory2.addDependsOn(propertiesComponentResolver.getId());
223        context.getComponentDefinitionRegistry().registerComponentDefinition(factory2);
224
225        MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class);
226        ctx.setId(contextId);
227        ctx.setRuntimeClass(BlueprintCamelContext.class);
228        ctx.setFactoryComponent(factory2);
229        ctx.setFactoryMethod("getContext");
230        ctx.setInitMethod("init");
231        ctx.setDestroyMethod("destroy");
232
233        // Register factory beans
234        registerBeans(context, contextId, ccfb.getThreadPools());
235        registerBeans(context, contextId, ccfb.getEndpoints());
236        registerBeans(context, contextId, ccfb.getRedeliveryPolicies());
237        registerBeans(context, contextId, ccfb.getBeans());
238
239        // Register processors
240        MutablePassThroughMetadata beanProcessorFactory = context.createMetadata(MutablePassThroughMetadata.class);
241        beanProcessorFactory.setId(".camelBlueprint.processor.bean.passThrough." + contextId);
242        beanProcessorFactory.setObject(new PassThroughCallable<Object>(new CamelInjector(contextId)));
243
244        MutableBeanMetadata beanProcessor = context.createMetadata(MutableBeanMetadata.class);
245        beanProcessor.setId(".camelBlueprint.processor.bean." + contextId);
246        beanProcessor.setRuntimeClass(CamelInjector.class);
247        beanProcessor.setFactoryComponent(beanProcessorFactory);
248        beanProcessor.setFactoryMethod("call");
249        beanProcessor.setProcessor(true);
250        beanProcessor.addProperty("blueprintContainer", createRef(context, "blueprintContainer"));
251        context.getComponentDefinitionRegistry().registerComponentDefinition(beanProcessor);
252
253        MutablePassThroughMetadata regProcessorFactory = context.createMetadata(MutablePassThroughMetadata.class);
254        regProcessorFactory.setId(".camelBlueprint.processor.registry.passThrough." + contextId);
255        regProcessorFactory.setObject(new PassThroughCallable<Object>(new CamelDependenciesFinder(contextId, context)));
256
257        MutableBeanMetadata regProcessor = context.createMetadata(MutableBeanMetadata.class);
258        regProcessor.setId(".camelBlueprint.processor.registry." + contextId);
259        regProcessor.setRuntimeClass(CamelDependenciesFinder.class);
260        regProcessor.setFactoryComponent(regProcessorFactory);
261        regProcessor.setFactoryMethod("call");
262        regProcessor.setProcessor(true);
263        regProcessor.addDependsOn(".camelBlueprint.processor.bean." + contextId);
264        regProcessor.addProperty("blueprintContainer", createRef(context, "blueprintContainer"));
265        context.getComponentDefinitionRegistry().registerComponentDefinition(regProcessor);
266
267        // lets inject the namespaces into any namespace aware POJOs
268        injectNamespaces(element, binder);
269
270        LOG.trace("Parsing CamelContext done, returning {}", ctx);
271        return ctx;
272    }
273
274    protected void injectNamespaces(Element element, Binder<Node> binder) {
275        NodeList list = element.getChildNodes();
276        Namespaces namespaces = null;
277        int size = list.getLength();
278        for (int i = 0; i < size; i++) {
279            Node child = list.item(i);
280            if (child instanceof Element) {
281                Element childElement = (Element) child;
282                Object object = binder.getJAXBNode(child);
283                if (object instanceof NamespaceAware) {
284                    NamespaceAware namespaceAware = (NamespaceAware) object;
285                    if (namespaces == null) {
286                        namespaces = new Namespaces(element);
287                    }
288                    namespaces.configure(namespaceAware);
289                }
290                injectNamespaces(childElement, binder);
291            }
292        }
293    }
294
295    private Metadata parseRouteContextNode(Element element, ParserContext context) {
296        LOG.trace("Parsing RouteContext {}", element);
297        // now parse the routes with JAXB
298        Binder<Node> binder;
299        try {
300            binder = getJaxbContext().createBinder();
301        } catch (JAXBException e) {
302            throw new ComponentDefinitionException("Failed to create the JAXB binder : " + e, e);
303        }
304        Object value = parseUsingJaxb(element, context, binder);
305        if (!(value instanceof CamelRouteContextFactoryBean)) {
306            throw new ComponentDefinitionException("Expected an instance of " + CamelRouteContextFactoryBean.class);
307        }
308
309        CamelRouteContextFactoryBean rcfb = (CamelRouteContextFactoryBean) value;
310        String id = rcfb.getId();
311
312        MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class);
313        factory.setId(".camelBlueprint.passThrough." + id);
314        factory.setObject(new PassThroughCallable<Object>(rcfb));
315
316        MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class);
317        factory2.setId(".camelBlueprint.factory." + id);
318        factory2.setFactoryComponent(factory);
319        factory2.setFactoryMethod("call");
320
321        MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class);
322        ctx.setId(id);
323        ctx.setRuntimeClass(List.class);
324        ctx.setFactoryComponent(factory2);
325        ctx.setFactoryMethod("getRoutes");
326        // must be lazy as we want CamelContext to be activated first
327        ctx.setActivation(ACTIVATION_LAZY);
328
329        // lets inject the namespaces into any namespace aware POJOs
330        injectNamespaces(element, binder);
331
332        LOG.trace("Parsing RouteContext done, returning {}", element, ctx);
333        return ctx;
334    }
335
336    private Metadata parseKeyStoreParametersNode(Element element, ParserContext context) {
337        LOG.trace("Parsing KeyStoreParameters {}", element);
338        // now parse the key store parameters with JAXB
339        Binder<Node> binder;
340        try {
341            binder = getJaxbContext().createBinder();
342        } catch (JAXBException e) {
343            throw new ComponentDefinitionException("Failed to create the JAXB binder : " + e, e);
344        }
345        Object value = parseUsingJaxb(element, context, binder);
346        if (!(value instanceof KeyStoreParametersFactoryBean)) {
347            throw new ComponentDefinitionException("Expected an instance of " + KeyStoreParametersFactoryBean.class);
348        }
349
350        KeyStoreParametersFactoryBean kspfb = (KeyStoreParametersFactoryBean) value;
351        String id = kspfb.getId();
352
353        MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class);
354        factory.setId(".camelBlueprint.passThrough." + id);
355        factory.setObject(new PassThroughCallable<Object>(kspfb));
356
357        MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class);
358        factory2.setId(".camelBlueprint.factory." + id);
359        factory2.setFactoryComponent(factory);
360        factory2.setFactoryMethod("call");
361        factory2.setInitMethod("afterPropertiesSet");
362        factory2.setDestroyMethod("destroy");
363        factory2.addProperty("blueprintContainer", createRef(context, "blueprintContainer"));
364
365        MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class);
366        ctx.setId(id);
367        ctx.setRuntimeClass(KeyStoreParameters.class);
368        ctx.setFactoryComponent(factory2);
369        ctx.setFactoryMethod("getObject");
370        // must be lazy as we want CamelContext to be activated first
371        ctx.setActivation(ACTIVATION_LAZY);
372
373        LOG.trace("Parsing KeyStoreParameters done, returning {}", ctx);
374        return ctx;
375    }
376
377    private Metadata parseSecureRandomParametersNode(Element element, ParserContext context) {
378        LOG.trace("Parsing SecureRandomParameters {}", element);
379        // now parse the key store parameters with JAXB
380        Binder<Node> binder;
381        try {
382            binder = getJaxbContext().createBinder();
383        } catch (JAXBException e) {
384            throw new ComponentDefinitionException("Failed to create the JAXB binder : " + e, e);
385        }
386        Object value = parseUsingJaxb(element, context, binder);
387        if (!(value instanceof SecureRandomParametersFactoryBean)) {
388            throw new ComponentDefinitionException("Expected an instance of " + SecureRandomParametersFactoryBean.class);
389        }
390
391        SecureRandomParametersFactoryBean srfb = (SecureRandomParametersFactoryBean) value;
392        String id = srfb.getId();
393
394        MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class);
395        factory.setId(".camelBlueprint.passThrough." + id);
396        factory.setObject(new PassThroughCallable<Object>(srfb));
397
398        MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class);
399        factory2.setId(".camelBlueprint.factory." + id);
400        factory2.setFactoryComponent(factory);
401        factory2.setFactoryMethod("call");
402        factory2.setInitMethod("afterPropertiesSet");
403        factory2.setDestroyMethod("destroy");
404        factory2.addProperty("blueprintContainer", createRef(context, "blueprintContainer"));
405
406        MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class);
407        ctx.setId(id);
408        ctx.setRuntimeClass(SecureRandomParameters.class);
409        ctx.setFactoryComponent(factory2);
410        ctx.setFactoryMethod("getObject");
411        // must be lazy as we want CamelContext to be activated first
412        ctx.setActivation(ACTIVATION_LAZY);
413
414        LOG.trace("Parsing SecureRandomParameters done, returning {}", ctx);
415        return ctx;
416    }
417
418    private Metadata parseSSLContextParametersNode(Element element, ParserContext context) {
419        LOG.trace("Parsing SSLContextParameters {}", element);
420        // now parse the key store parameters with JAXB
421        Binder<Node> binder;
422        try {
423            binder = getJaxbContext().createBinder();
424        } catch (JAXBException e) {
425            throw new ComponentDefinitionException("Failed to create the JAXB binder : " + e, e);
426        }
427        Object value = parseUsingJaxb(element, context, binder);
428        if (!(value instanceof SSLContextParametersFactoryBean)) {
429            throw new ComponentDefinitionException("Expected an instance of " + SSLContextParametersFactoryBean.class);
430        }
431
432        SSLContextParametersFactoryBean scpfb = (SSLContextParametersFactoryBean) value;
433        String id = scpfb.getId();
434
435        MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class);
436        factory.setId(".camelBlueprint.passThrough." + id);
437        factory.setObject(new PassThroughCallable<Object>(scpfb));
438
439        MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class);
440        factory2.setId(".camelBlueprint.factory." + id);
441        factory2.setFactoryComponent(factory);
442        factory2.setFactoryMethod("call");
443        factory2.setInitMethod("afterPropertiesSet");
444        factory2.setDestroyMethod("destroy");
445        factory2.addProperty("blueprintContainer", createRef(context, "blueprintContainer"));
446
447        MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class);
448        ctx.setId(id);
449        ctx.setRuntimeClass(SSLContextParameters.class);
450        ctx.setFactoryComponent(factory2);
451        ctx.setFactoryMethod("getObject");
452        // must be lazy as we want CamelContext to be activated first
453        ctx.setActivation(ACTIVATION_LAZY);
454
455        LOG.trace("Parsing SSLContextParameters done, returning {}", ctx);
456        return ctx;
457    }
458
459    private void registerBeans(ParserContext context, String contextId, List<?> beans) {
460        if (beans != null) {
461            for (Object bean : beans) {
462                if (bean instanceof AbstractCamelFactoryBean) {
463                    registerBean(context, contextId, (AbstractCamelFactoryBean<?>) bean);
464                }
465            }
466        }
467    }
468
469    protected void registerBean(ParserContext context, String contextId, AbstractCamelFactoryBean<?> fact) {
470        String id = fact.getId();
471
472        fact.setCamelContextId(contextId);
473
474        MutablePassThroughMetadata eff = context.createMetadata(MutablePassThroughMetadata.class);
475        eff.setId(".camelBlueprint.bean.passthrough." + id);
476        eff.setObject(new PassThroughCallable<Object>(fact));
477
478        MutableBeanMetadata ef = context.createMetadata(MutableBeanMetadata.class);
479        ef.setId(".camelBlueprint.bean.factory." + id);
480        ef.setFactoryComponent(eff);
481        ef.setFactoryMethod("call");
482        ef.addProperty("blueprintContainer", createRef(context, "blueprintContainer"));
483        ef.setInitMethod("afterPropertiesSet");
484        ef.setDestroyMethod("destroy");
485
486        MutableBeanMetadata e = context.createMetadata(MutableBeanMetadata.class);
487        e.setId(id);
488        e.setRuntimeClass(fact.getObjectType());
489        e.setFactoryComponent(ef);
490        e.setFactoryMethod("getObject");
491        e.addDependsOn(".camelBlueprint.processor.bean." + contextId);
492
493        context.getComponentDefinitionRegistry().registerComponentDefinition(e);
494    }
495
496    protected BlueprintContainer getBlueprintContainer(ParserContext context) {
497        PassThroughMetadata ptm = (PassThroughMetadata) context.getComponentDefinitionRegistry().getComponentDefinition("blueprintContainer");
498        return (BlueprintContainer) ptm.getObject();
499    }
500
501    public ComponentMetadata decorate(Node node, ComponentMetadata component, ParserContext context) {
502        return null;
503    }
504
505    protected Object parseUsingJaxb(Element element, ParserContext parserContext, Binder<Node> binder) {
506        try {
507            return binder.unmarshal(element);
508        } catch (JAXBException e) {
509            throw new ComponentDefinitionException("Failed to parse JAXB element: " + e, e);
510        }
511    }
512
513    public JAXBContext getJaxbContext() throws JAXBException {
514        if (jaxbContext == null) {
515            jaxbContext = createJaxbContext();
516        }
517        return jaxbContext;
518    }
519
520    protected JAXBContext createJaxbContext() throws JAXBException {
521        StringBuilder packages = new StringBuilder();
522        for (Class<?> cl : getJaxbPackages()) {
523            if (packages.length() > 0) {
524                packages.append(":");
525            }
526            packages.append(cl.getName().substring(0, cl.getName().lastIndexOf('.')));
527        }
528        return JAXBContext.newInstance(packages.toString(), getClass().getClassLoader());
529    }
530
531    protected Set<Class<?>> getJaxbPackages() {
532        Set<Class<?>> classes = new HashSet<Class<?>>();
533        classes.add(CamelContextFactoryBean.class);
534        classes.add(AbstractCamelContextFactoryBean.class);
535        classes.add(org.apache.camel.ExchangePattern.class);
536        classes.add(org.apache.camel.model.RouteDefinition.class);
537        classes.add(org.apache.camel.model.config.StreamResequencerConfig.class);
538        classes.add(org.apache.camel.model.dataformat.DataFormatsDefinition.class);
539        classes.add(org.apache.camel.model.language.ExpressionDefinition.class);
540        classes.add(org.apache.camel.model.loadbalancer.RoundRobinLoadBalancerDefinition.class);
541        classes.add(SSLContextParametersFactoryBean.class);
542        return classes;
543    }
544
545    private RefMetadata createRef(ParserContext context, String value) {
546        MutableRefMetadata r = context.createMetadata(MutableRefMetadata.class);
547        r.setComponentId(value);
548        return r;
549    }
550
551    private static ComponentMetadata getDataformatResolverReference(ParserContext context, String dataformat) {
552        ComponentDefinitionRegistry componentDefinitionRegistry = context.getComponentDefinitionRegistry();
553        ComponentMetadata cm = componentDefinitionRegistry.getComponentDefinition(".camelBlueprint.dataformatResolver." + dataformat);
554        if (cm == null) {
555            MutableReferenceMetadata svc = context.createMetadata(MutableReferenceMetadata.class);
556            svc.setId(".camelBlueprint.dataformatResolver." + dataformat);
557            svc.setFilter("(dataformat=" + dataformat + ")");
558            svc.setAvailability(componentDefinitionRegistry.containsComponentDefinition(dataformat) ? AVAILABILITY_OPTIONAL : AVAILABILITY_MANDATORY);
559            try {
560                // Try to set the runtime interface (only with aries blueprint > 0.1
561                svc.getClass().getMethod("setRuntimeInterface", Class.class).invoke(svc, DataFormatResolver.class);
562            } catch (Throwable t) {
563                // Check if the bundle can see the class
564                try {
565                    PassThroughMetadata ptm = (PassThroughMetadata) componentDefinitionRegistry.getComponentDefinition("blueprintBundle");
566                    Bundle b = (Bundle) ptm.getObject();
567                    if (b.loadClass(DataFormatResolver.class.getName()) != DataFormatResolver.class) {
568                        throw new UnsupportedOperationException();
569                    }
570                    svc.setInterface(DataFormatResolver.class.getName());
571                } catch (Throwable t2) {
572                    throw new UnsupportedOperationException();
573                }
574            }
575            componentDefinitionRegistry.registerComponentDefinition(svc);
576            cm = svc;
577        }
578        return cm;
579    }
580
581    private static ComponentMetadata getLanguageResolverReference(ParserContext context, String language) {
582        ComponentDefinitionRegistry componentDefinitionRegistry = context.getComponentDefinitionRegistry();
583        ComponentMetadata cm = componentDefinitionRegistry.getComponentDefinition(".camelBlueprint.languageResolver." + language);
584        if (cm == null) {
585            MutableReferenceMetadata svc = context.createMetadata(MutableReferenceMetadata.class);
586            svc.setId(".camelBlueprint.languageResolver." + language);
587            svc.setFilter("(language=" + language + ")");
588            svc.setAvailability(componentDefinitionRegistry.containsComponentDefinition(language) ? AVAILABILITY_OPTIONAL : AVAILABILITY_MANDATORY);
589            try {
590                // Try to set the runtime interface (only with aries blueprint > 0.1
591                svc.getClass().getMethod("setRuntimeInterface", Class.class).invoke(svc, LanguageResolver.class);
592            } catch (Throwable t) {
593                // Check if the bundle can see the class
594                try {
595                    PassThroughMetadata ptm = (PassThroughMetadata) componentDefinitionRegistry.getComponentDefinition("blueprintBundle");
596                    Bundle b = (Bundle) ptm.getObject();
597                    if (b.loadClass(LanguageResolver.class.getName()) != LanguageResolver.class) {
598                        throw new UnsupportedOperationException();
599                    }
600                    svc.setInterface(LanguageResolver.class.getName());
601                } catch (Throwable t2) {
602                    throw new UnsupportedOperationException();
603                }
604            }
605            componentDefinitionRegistry.registerComponentDefinition(svc);
606            cm = svc;
607        }
608        return cm;
609    }
610
611    private static ComponentMetadata getComponentResolverReference(ParserContext context, String component) {
612        ComponentDefinitionRegistry componentDefinitionRegistry = context.getComponentDefinitionRegistry();
613        ComponentMetadata cm = componentDefinitionRegistry.getComponentDefinition(".camelBlueprint.componentResolver." + component);
614        if (cm == null) {
615            MutableReferenceMetadata svc = context.createMetadata(MutableReferenceMetadata.class);
616            svc.setId(".camelBlueprint.componentResolver." + component);
617            svc.setFilter("(component=" + component + ")");
618            svc.setAvailability(componentDefinitionRegistry.containsComponentDefinition(component) ? AVAILABILITY_OPTIONAL : AVAILABILITY_MANDATORY);
619            try {
620                // Try to set the runtime interface (only with aries blueprint > 0.1
621                svc.getClass().getMethod("setRuntimeInterface", Class.class).invoke(svc, ComponentResolver.class);
622            } catch (Throwable t) {
623                // Check if the bundle can see the class
624                try {
625                    PassThroughMetadata ptm = (PassThroughMetadata) componentDefinitionRegistry.getComponentDefinition("blueprintBundle");
626                    Bundle b = (Bundle) ptm.getObject();
627                    if (b.loadClass(ComponentResolver.class.getName()) != ComponentResolver.class) {
628                        throw new UnsupportedOperationException();
629                    }
630                    svc.setInterface(ComponentResolver.class.getName());
631                } catch (Throwable t2) {
632                    throw new UnsupportedOperationException();
633                }
634            }
635            componentDefinitionRegistry.registerComponentDefinition(svc);
636            cm = svc;
637        }
638        return cm;
639    }
640
641    public static class PassThroughCallable<T> implements Callable<T> {
642
643        private T value;
644
645        public PassThroughCallable(T value) {
646            this.value = value;
647        }
648
649        public T call() throws Exception {
650            return value;
651        }
652    }
653
654    public static class CamelInjector extends CamelPostProcessorHelper implements BeanProcessor {
655
656        private final String camelContextName;
657        private BlueprintContainer blueprintContainer;
658
659        public CamelInjector(String camelContextName) {
660            this.camelContextName = camelContextName;
661        }
662
663        public void setBlueprintContainer(BlueprintContainer blueprintContainer) {
664            this.blueprintContainer = blueprintContainer;
665        }
666
667        @Override
668        public CamelContext getCamelContext() {
669            if (blueprintContainer != null) {
670                CamelContext answer = (CamelContext) blueprintContainer.getComponentInstance(camelContextName);
671                return answer;
672            }
673            return null;
674        }
675
676        public Object beforeInit(Object bean, String beanName, BeanCreator beanCreator, BeanMetadata beanMetadata) {
677            LOG.trace("Before init of bean: {} -> {}", beanName, bean);
678            // prefer to inject later in afterInit
679            return bean;
680        }
681
682        /**
683         * A strategy method to allow implementations to perform some custom JBI
684         * based injection of the POJO
685         *
686         * @param bean the bean to be injected
687         */
688        protected void injectFields(final Object bean, final String beanName) {
689            Class<?> clazz = bean.getClass();
690            do {
691                Field[] fields = clazz.getDeclaredFields();
692                for (Field field : fields) {
693                    PropertyInject propertyInject = field.getAnnotation(PropertyInject.class);
694                    if (propertyInject != null && matchContext(propertyInject.context())) {
695                        injectFieldProperty(field, propertyInject.value(), propertyInject.defaultValue(), bean, beanName);
696                    }
697
698                    BeanInject beanInject = field.getAnnotation(BeanInject.class);
699                    if (beanInject != null && matchContext(beanInject.context())) {
700                        injectFieldBean(field, beanInject.value(), bean, beanName);
701                    }
702
703                    EndpointInject endpointInject = field.getAnnotation(EndpointInject.class);
704                    if (endpointInject != null && matchContext(endpointInject.context())) {
705                        injectField(field, endpointInject.uri(), endpointInject.ref(), endpointInject.property(), bean, beanName);
706                    }
707
708                    Produce produce = field.getAnnotation(Produce.class);
709                    if (produce != null && matchContext(produce.context())) {
710                        injectField(field, produce.uri(), produce.ref(), produce.property(), bean, beanName);
711                    }
712                }
713                clazz = clazz.getSuperclass();
714            } while (clazz != null && clazz != Object.class);
715        }
716
717        protected void injectField(Field field, String endpointUri, String endpointRef, String endpointProperty, Object bean, String beanName) {
718            setField(field, bean, getInjectionValue(field.getType(), endpointUri, endpointRef, endpointProperty, field.getName(), bean, beanName));
719        }
720
721        protected void injectFieldProperty(Field field, String propertyName, String propertyDefaultValue, Object bean, String beanName) {
722            setField(field, bean, getInjectionPropertyValue(field.getType(), propertyName, propertyDefaultValue, field.getName(), bean, beanName));
723        }
724
725        public void injectFieldBean(Field field, String name, Object bean, String beanName) {
726            setField(field, bean, getInjectionBeanValue(field.getType(), name));
727        }
728
729        protected static void setField(Field field, Object instance, Object value) {
730            try {
731                boolean oldAccessible = field.isAccessible();
732                boolean shouldSetAccessible = !Modifier.isPublic(field.getModifiers()) && !oldAccessible;
733                if (shouldSetAccessible) {
734                    field.setAccessible(true);
735                }
736                field.set(instance, value);
737                if (shouldSetAccessible) {
738                    field.setAccessible(oldAccessible);
739                }
740            } catch (IllegalArgumentException ex) {
741                throw new UnsupportedOperationException("Cannot inject value of class: " + value.getClass() + " into: " + field);
742            } catch (IllegalAccessException ex) {
743                throw new IllegalStateException("Could not access method: " + ex.getMessage());
744            }
745        }
746
747        protected void injectMethods(final Object bean, final String beanName) {
748            Class<?> clazz = bean.getClass();
749            do {
750                Method[] methods = clazz.getDeclaredMethods();
751                for (Method method : methods) {
752                    setterInjection(method, bean, beanName);
753                    consumerInjection(method, bean, beanName);
754                }
755                clazz = clazz.getSuperclass();
756            } while (clazz != null && clazz != Object.class);
757        }
758
759        protected void setterInjection(Method method, Object bean, String beanName) {
760            PropertyInject propertyInject = method.getAnnotation(PropertyInject.class);
761            if (propertyInject != null && matchContext(propertyInject.context())) {
762                setterPropertyInjection(method, propertyInject.value(), propertyInject.defaultValue(), bean, beanName);
763            }
764
765            BeanInject beanInject = method.getAnnotation(BeanInject.class);
766            if (beanInject != null && matchContext(beanInject.context())) {
767                setterBeanInjection(method, beanInject.value(), bean, beanName);
768            }
769
770            EndpointInject endpointInject = method.getAnnotation(EndpointInject.class);
771            if (endpointInject != null && matchContext(endpointInject.context())) {
772                setterInjection(method, bean, beanName, endpointInject.uri(), endpointInject.ref(), endpointInject.property());
773            }
774
775            Produce produce = method.getAnnotation(Produce.class);
776            if (produce != null && matchContext(produce.context())) {
777                setterInjection(method, bean, beanName, produce.uri(), produce.ref(), produce.property());
778            }
779        }
780
781        protected void setterPropertyInjection(Method method, String propertyValue, String propertyDefaultValue, Object bean, String beanName) {
782            Class<?>[] parameterTypes = method.getParameterTypes();
783            if (parameterTypes != null) {
784                if (parameterTypes.length != 1) {
785                    LOG.warn("Ignoring badly annotated method for injection due to incorrect number of parameters: " + method);
786                } else {
787                    String propertyName = ObjectHelper.getPropertyName(method);
788                    Object value = getInjectionPropertyValue(parameterTypes[0], propertyValue, propertyDefaultValue, propertyName, bean, beanName);
789                    ObjectHelper.invokeMethod(method, bean, value);
790                }
791            }
792        }
793
794        protected void setterBeanInjection(Method method, String name, Object bean, String beanName) {
795            Class<?>[] parameterTypes = method.getParameterTypes();
796            if (parameterTypes != null) {
797                if (parameterTypes.length != 1) {
798                    LOG.warn("Ignoring badly annotated method for injection due to incorrect number of parameters: " + method);
799                } else {
800                    Object value = getInjectionBeanValue(parameterTypes[0], name);
801                    ObjectHelper.invokeMethod(method, bean, value);
802                }
803            }
804        }
805
806        protected void setterInjection(Method method, Object bean, String beanName, String endpointUri, String endpointRef, String endpointProperty) {
807            Class<?>[] parameterTypes = method.getParameterTypes();
808            if (parameterTypes != null) {
809                if (parameterTypes.length != 1) {
810                    LOG.warn("Ignoring badly annotated method for injection due to incorrect number of parameters: " + method);
811                } else {
812                    String propertyName = ObjectHelper.getPropertyName(method);
813                    Object value = getInjectionValue(parameterTypes[0], endpointUri, endpointRef, endpointProperty, propertyName, bean, beanName);
814                    ObjectHelper.invokeMethod(method, bean, value);
815                }
816            }
817        }
818
819        public Object afterInit(Object bean, String beanName, BeanCreator beanCreator, BeanMetadata beanMetadata) {
820            LOG.trace("After init of bean: {} -> {}", beanName, bean);
821            // we cannot inject CamelContextAware beans as the CamelContext may not be ready
822            injectFields(bean, beanName);
823            injectMethods(bean, beanName);
824            return bean;
825        }
826
827        public void beforeDestroy(Object bean, String beanName) {
828        }
829
830        public void afterDestroy(Object bean, String beanName) {
831        }
832
833        @Override
834        protected boolean isSingleton(Object bean, String beanName) {
835            ComponentMetadata meta = blueprintContainer.getComponentMetadata(beanName);
836            if (meta != null && meta instanceof BeanMetadata) {
837                String scope = ((BeanMetadata) meta).getScope();
838                if (scope != null) {
839                    return BeanMetadata.SCOPE_SINGLETON.equals(scope);
840                }
841            }
842            // fallback to super, which will assume singleton
843            // for beans not implementing Camel's IsSingleton interface
844            return super.isSingleton(bean, beanName);
845        }
846    }
847
848    public static class CamelDependenciesFinder implements ComponentDefinitionRegistryProcessor {
849
850        private final String camelContextName;
851        private final ParserContext context;
852        private BlueprintContainer blueprintContainer;
853
854        public CamelDependenciesFinder(String camelContextName, ParserContext context) {
855            this.camelContextName = camelContextName;
856            this.context = context;
857        }
858
859        public void setBlueprintContainer(BlueprintContainer blueprintContainer) {
860            this.blueprintContainer = blueprintContainer;
861        }
862
863        @SuppressWarnings("deprecation")
864        public void process(ComponentDefinitionRegistry componentDefinitionRegistry) {
865            CamelContextFactoryBean ccfb = (CamelContextFactoryBean) blueprintContainer.getComponentInstance(".camelBlueprint.factory." + camelContextName);
866            CamelContext camelContext = ccfb.getContext();
867
868            Set<String> components = new HashSet<String>();
869            Set<String> languages = new HashSet<String>();
870            Set<String> dataformats = new HashSet<String>();
871            for (RouteDefinition rd : camelContext.getRouteDefinitions()) {
872                findInputComponents(rd.getInputs(), components, languages, dataformats);
873                findOutputComponents(rd.getOutputs(), components, languages, dataformats);
874            }
875            // We can only add service references to resolvers, but we can't make the factory depends on those
876            // because the factory has already been instantiated
877            try {
878                for (String component : components) {
879                    getComponentResolverReference(context, component);
880                }
881                for (String language : languages) {
882                    getLanguageResolverReference(context, language);
883                }
884                for (String dataformat : dataformats) {
885                    getDataformatResolverReference(context, dataformat);
886                }
887            } catch (UnsupportedOperationException e) {
888                LOG.warn("Unable to add dependencies to Camel components OSGi services. "
889                    + "The Apache Aries blueprint implementation used is too old and the blueprint bundle can not see the org.apache.camel.spi package.");
890                components.clear();
891                languages.clear();
892                dataformats.clear();
893            }
894
895        }
896
897        private void findInputComponents(List<FromDefinition> defs, Set<String> components, Set<String> languages, Set<String> dataformats) {
898            if (defs != null) {
899                for (FromDefinition def : defs) {
900                    findUriComponent(def.getUri(), components);
901                    findSchedulerUriComponent(def.getUri(), components);
902                }
903            }
904        }
905
906        @SuppressWarnings({"rawtypes"})
907        private void findOutputComponents(List<ProcessorDefinition<?>> defs, Set<String> components, Set<String> languages, Set<String> dataformats) {
908            if (defs != null) {
909                for (ProcessorDefinition<?> def : defs) {
910                    if (def instanceof SendDefinition) {
911                        findUriComponent(((SendDefinition) def).getUri(), components);
912                    }
913                    if (def instanceof MarshalDefinition) {
914                        findDataFormat(((MarshalDefinition) def).getDataFormatType(), dataformats);
915                    }
916                    if (def instanceof UnmarshalDefinition) {
917                        findDataFormat(((UnmarshalDefinition) def).getDataFormatType(), dataformats);
918                    }
919                    if (def instanceof ExpressionNode) {
920                        findLanguage(((ExpressionNode) def).getExpression(), languages);
921                    }
922                    if (def instanceof ResequenceDefinition) {
923                        findLanguage(((ResequenceDefinition) def).getExpression(), languages);
924                    }
925                    if (def instanceof AggregateDefinition) {
926                        findLanguage(((AggregateDefinition) def).getExpression(), languages);
927                        findLanguage(((AggregateDefinition) def).getCorrelationExpression(), languages);
928                        findLanguage(((AggregateDefinition) def).getCompletionPredicate(), languages);
929                        findLanguage(((AggregateDefinition) def).getCompletionTimeoutExpression(), languages);
930                        findLanguage(((AggregateDefinition) def).getCompletionSizeExpression(), languages);
931                    }
932                    if (def instanceof CatchDefinition) {
933                        findLanguage(((CatchDefinition) def).getHandled(), languages);
934                    }
935                    if (def instanceof OnExceptionDefinition) {
936                        findLanguage(((OnExceptionDefinition) def).getRetryWhile(), languages);
937                        findLanguage(((OnExceptionDefinition) def).getHandled(), languages);
938                        findLanguage(((OnExceptionDefinition) def).getContinued(), languages);
939                    }
940                    if (def instanceof SortDefinition) {
941                        findLanguage(((SortDefinition) def).getExpression(), languages);
942                    }
943                    if (def instanceof WireTapDefinition) {
944                        findLanguage(((WireTapDefinition<?>) def).getNewExchangeExpression(), languages);
945                    }
946                    findOutputComponents(def.getOutputs(), components, languages, dataformats);
947                }
948            }
949        }
950
951        private void findLanguage(ExpressionDefinition expression, Set<String> languages) {
952            if (expression != null) {
953                String lang = expression.getLanguage();
954                if (lang != null && lang.length() > 0) {
955                    languages.add(lang);
956                }
957            }
958        }
959
960        private void findLanguage(ExpressionSubElementDefinition expression, Set<String> languages) {
961            if (expression != null) {
962                findLanguage(expression.getExpressionType(), languages);
963            }
964        }
965
966        private void findDataFormat(DataFormatDefinition dfd, Set<String> dataformats) {
967            if (dfd != null && dfd.getDataFormatName() != null) {
968                dataformats.add(dfd.getDataFormatName());
969            }
970        }
971
972        private void findUriComponent(String uri, Set<String> components) {
973            if (uri != null) {
974                String splitURI[] = ObjectHelper.splitOnCharacter(uri, ":", 2);
975                if (splitURI[1] != null) {
976                    String scheme = splitURI[0];
977                    components.add(scheme);
978                }
979            }
980        }
981
982        private void findSchedulerUriComponent(String uri, Set<String> components) {
983
984            // the input may use a scheduler which can be quartz or spring
985            if (uri != null) {
986                try {
987                    URI u = new URI(uri);
988                    Map<String, Object> parameters = URISupport.parseParameters(u);
989                    Object value = parameters.get("scheduler");
990                    if (value == null) {
991                        value = parameters.get("consumer.scheduler");
992                    }
993                    if (value != null) {
994                        // the scheduler can be quartz2 or spring based, so add reference to camel component
995                        // from these components os blueprint knows about the requirement
996                        String name = value.toString();
997                        if ("quartz2".equals(name)) {
998                            components.add("quartz2");
999                        } else if ("spring".equals(name)) {
1000                            components.add("spring-event");
1001                        }
1002                    }
1003                } catch (URISyntaxException e) {
1004                    // ignore
1005                }
1006            }
1007        }
1008
1009    }
1010
1011}