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