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