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