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