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}