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