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