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