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