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