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.BeanInject;
049    import org.apache.camel.CamelContext;
050    import org.apache.camel.EndpointInject;
051    import org.apache.camel.Produce;
052    import org.apache.camel.PropertyInject;
053    import org.apache.camel.blueprint.BlueprintCamelContext;
054    import org.apache.camel.blueprint.CamelContextFactoryBean;
055    import org.apache.camel.blueprint.CamelRouteContextFactoryBean;
056    import org.apache.camel.builder.xml.Namespaces;
057    import org.apache.camel.core.xml.AbstractCamelContextFactoryBean;
058    import org.apache.camel.core.xml.AbstractCamelFactoryBean;
059    import org.apache.camel.impl.CamelPostProcessorHelper;
060    import org.apache.camel.impl.DefaultCamelContextNameStrategy;
061    import org.apache.camel.model.AggregateDefinition;
062    import org.apache.camel.model.CatchDefinition;
063    import org.apache.camel.model.DataFormatDefinition;
064    import org.apache.camel.model.ExpressionNode;
065    import org.apache.camel.model.ExpressionSubElementDefinition;
066    import org.apache.camel.model.FromDefinition;
067    import org.apache.camel.model.MarshalDefinition;
068    import org.apache.camel.model.OnExceptionDefinition;
069    import org.apache.camel.model.ProcessorDefinition;
070    import org.apache.camel.model.ResequenceDefinition;
071    import org.apache.camel.model.RouteDefinition;
072    import org.apache.camel.model.SendDefinition;
073    import org.apache.camel.model.SortDefinition;
074    import org.apache.camel.model.UnmarshalDefinition;
075    import org.apache.camel.model.WireTapDefinition;
076    import org.apache.camel.model.language.ExpressionDefinition;
077    import org.apache.camel.spi.CamelContextNameStrategy;
078    import org.apache.camel.spi.ComponentResolver;
079    import org.apache.camel.spi.DataFormatResolver;
080    import org.apache.camel.spi.LanguageResolver;
081    import org.apache.camel.spi.NamespaceAware;
082    import org.apache.camel.util.ObjectHelper;
083    import org.apache.camel.util.blueprint.KeyStoreParametersFactoryBean;
084    import org.apache.camel.util.blueprint.SSLContextParametersFactoryBean;
085    import org.apache.camel.util.blueprint.SecureRandomParametersFactoryBean;
086    import org.apache.camel.util.jsse.KeyStoreParameters;
087    import org.apache.camel.util.jsse.SSLContextParameters;
088    import org.apache.camel.util.jsse.SecureRandomParameters;
089    
090    import org.osgi.framework.Bundle;
091    import org.osgi.service.blueprint.container.BlueprintContainer;
092    import org.osgi.service.blueprint.container.ComponentDefinitionException;
093    import org.osgi.service.blueprint.reflect.BeanMetadata;
094    import org.osgi.service.blueprint.reflect.ComponentMetadata;
095    import org.osgi.service.blueprint.reflect.Metadata;
096    import org.osgi.service.blueprint.reflect.RefMetadata;
097    import org.slf4j.Logger;
098    import org.slf4j.LoggerFactory;
099    
100    import static org.osgi.service.blueprint.reflect.ComponentMetadata.ACTIVATION_LAZY;
101    import static org.osgi.service.blueprint.reflect.ServiceReferenceMetadata.AVAILABILITY_MANDATORY;
102    import static org.osgi.service.blueprint.reflect.ServiceReferenceMetadata.AVAILABILITY_OPTIONAL;
103    
104    /**
105     * Camel {@link NamespaceHandler} to parse the Camel related namespaces.
106     */
107    public 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    }