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