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.util; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.net.URL; 022import java.util.Enumeration; 023import java.util.Iterator; 024import java.util.List; 025import java.util.Locale; 026import java.util.Map; 027import java.util.Properties; 028import java.util.Set; 029import java.util.SortedMap; 030import java.util.StringTokenizer; 031import java.util.TreeMap; 032 033import org.apache.camel.CamelContext; 034import org.apache.camel.Component; 035import org.apache.camel.Endpoint; 036import org.apache.camel.Exchange; 037import org.apache.camel.NoSuchBeanException; 038import org.apache.camel.NoSuchEndpointException; 039import org.apache.camel.component.properties.PropertiesComponent; 040import org.apache.camel.model.FromDefinition; 041import org.apache.camel.model.ProcessorDefinition; 042import org.apache.camel.model.ProcessorDefinitionHelper; 043import org.apache.camel.model.RouteDefinition; 044import org.apache.camel.spi.ClassResolver; 045import org.apache.camel.spi.RouteStartupOrder; 046import org.slf4j.Logger; 047import org.slf4j.LoggerFactory; 048 049import static org.apache.camel.util.ObjectHelper.isEmpty; 050import static org.apache.camel.util.ObjectHelper.isNotEmpty; 051import static org.apache.camel.util.ObjectHelper.notNull; 052 053/** 054 * A number of helper methods 055 * 056 * @version 057 */ 058public final class CamelContextHelper { 059 public static final String COMPONENT_BASE = "META-INF/services/org/apache/camel/component/"; 060 public static final String COMPONENT_DESCRIPTOR = "META-INF/services/org/apache/camel/component.properties"; 061 public static final String COMPONENT_DOCUMENTATION_PREFIX = "org/apache/camel/component/"; 062 public static final String MODEL_DESCRIPTOR = "META-INF/services/org/apache/camel/model.properties"; 063 public static final String MODEL_DOCUMENTATION_PREFIX = "org/apache/camel/model/"; 064 065 private static final Logger LOG = LoggerFactory.getLogger(CamelContextHelper.class); 066 067 /** 068 * Utility classes should not have a public constructor. 069 */ 070 private CamelContextHelper() { 071 } 072 073 /** 074 * Returns the mandatory endpoint for the given URI or the 075 * {@link org.apache.camel.NoSuchEndpointException} is thrown 076 */ 077 public static Endpoint getMandatoryEndpoint(CamelContext camelContext, String uri) 078 throws NoSuchEndpointException { 079 Endpoint endpoint = camelContext.getEndpoint(uri); 080 if (endpoint == null) { 081 throw new NoSuchEndpointException(uri); 082 } else { 083 return endpoint; 084 } 085 } 086 087 /** 088 * Returns the mandatory endpoint for the given URI and type or the 089 * {@link org.apache.camel.NoSuchEndpointException} is thrown 090 */ 091 public static <T extends Endpoint> T getMandatoryEndpoint(CamelContext camelContext, String uri, Class<T> type) { 092 Endpoint endpoint = getMandatoryEndpoint(camelContext, uri); 093 return ObjectHelper.cast(type, endpoint); 094 } 095 096 /** 097 * Converts the given value to the requested type 098 */ 099 public static <T> T convertTo(CamelContext context, Class<T> type, Object value) { 100 notNull(context, "camelContext"); 101 return context.getTypeConverter().convertTo(type, value); 102 } 103 104 /** 105 * Converts the given value to the specified type throwing an {@link IllegalArgumentException} 106 * if the value could not be converted to a non null value 107 */ 108 public static <T> T mandatoryConvertTo(CamelContext context, Class<T> type, Object value) { 109 T answer = convertTo(context, type, value); 110 if (answer == null) { 111 throw new IllegalArgumentException("Value " + value + " converted to " + type.getName() + " cannot be null"); 112 } 113 return answer; 114 } 115 116 /** 117 * Creates a new instance of the given type using the {@link org.apache.camel.spi.Injector} on the given 118 * {@link CamelContext} 119 */ 120 public static <T> T newInstance(CamelContext context, Class<T> beanType) { 121 return context.getInjector().newInstance(beanType); 122 } 123 124 /** 125 * Look up the given named bean in the {@link org.apache.camel.spi.Registry} on the 126 * {@link CamelContext} 127 */ 128 public static Object lookup(CamelContext context, String name) { 129 return context.getRegistry().lookupByName(name); 130 } 131 132 /** 133 * Look up the given named bean of the given type in the {@link org.apache.camel.spi.Registry} on the 134 * {@link CamelContext} 135 */ 136 public static <T> T lookup(CamelContext context, String name, Class<T> beanType) { 137 return context.getRegistry().lookupByNameAndType(name, beanType); 138 } 139 140 /** 141 * Look up the given named bean in the {@link org.apache.camel.spi.Registry} on the 142 * {@link CamelContext} or throws {@link NoSuchBeanException} if not found. 143 */ 144 public static Object mandatoryLookup(CamelContext context, String name) { 145 Object answer = lookup(context, name); 146 if (answer == null) { 147 throw new NoSuchBeanException(name); 148 } 149 return answer; 150 } 151 152 /** 153 * Look up the given named bean of the given type in the {@link org.apache.camel.spi.Registry} on the 154 * {@link CamelContext} or throws NoSuchBeanException if not found. 155 */ 156 public static <T> T mandatoryLookup(CamelContext context, String name, Class<T> beanType) { 157 T answer = lookup(context, name, beanType); 158 if (answer == null) { 159 throw new NoSuchBeanException(name, beanType.getName()); 160 } 161 return answer; 162 } 163 164 /** 165 * Evaluates the @EndpointInject annotation using the given context 166 */ 167 public static Endpoint getEndpointInjection(CamelContext camelContext, String uri, String ref, String injectionPointName, boolean mandatory) { 168 if (ObjectHelper.isNotEmpty(uri) && ObjectHelper.isNotEmpty(ref)) { 169 throw new IllegalArgumentException("Both uri and name is provided, only either one is allowed: uri=" + uri + ", ref=" + ref); 170 } 171 172 Endpoint endpoint; 173 if (isNotEmpty(uri)) { 174 endpoint = camelContext.getEndpoint(uri); 175 } else { 176 // if a ref is given then it should be possible to lookup 177 // otherwise we do not catch situations where there is a typo etc 178 if (isNotEmpty(ref)) { 179 endpoint = mandatoryLookup(camelContext, ref, Endpoint.class); 180 } else { 181 if (isEmpty(ref)) { 182 ref = injectionPointName; 183 } 184 if (mandatory) { 185 endpoint = mandatoryLookup(camelContext, ref, Endpoint.class); 186 } else { 187 endpoint = lookup(camelContext, ref, Endpoint.class); 188 } 189 } 190 } 191 return endpoint; 192 } 193 194 /** 195 * Gets the maximum cache pool size. 196 * <p/> 197 * Will use the property set on CamelContext with the key {@link Exchange#MAXIMUM_CACHE_POOL_SIZE}. 198 * If no property has been set, then it will fallback to return a size of 1000. 199 * 200 * @param camelContext the camel context 201 * @return the maximum cache size 202 * @throws IllegalArgumentException is thrown if the property is illegal 203 */ 204 public static int getMaximumCachePoolSize(CamelContext camelContext) throws IllegalArgumentException { 205 if (camelContext != null) { 206 String s = camelContext.getProperty(Exchange.MAXIMUM_CACHE_POOL_SIZE); 207 if (s != null) { 208 try { 209 // we cannot use Camel type converters as they may not be ready this early 210 Integer size = Integer.valueOf(s); 211 if (size == null || size <= 0) { 212 throw new IllegalArgumentException("Property " + Exchange.MAXIMUM_CACHE_POOL_SIZE + " must be a positive number, was: " + s); 213 } 214 return size; 215 } catch (NumberFormatException e) { 216 throw new IllegalArgumentException("Property " + Exchange.MAXIMUM_CACHE_POOL_SIZE + " must be a positive number, was: " + s, e); 217 } 218 } 219 } 220 221 // 1000 is the default fallback 222 return 1000; 223 } 224 225 /** 226 * Gets the maximum endpoint cache size. 227 * <p/> 228 * Will use the property set on CamelContext with the key {@link Exchange#MAXIMUM_ENDPOINT_CACHE_SIZE}. 229 * If no property has been set, then it will fallback to return a size of 1000. 230 * 231 * @param camelContext the camel context 232 * @return the maximum cache size 233 * @throws IllegalArgumentException is thrown if the property is illegal 234 */ 235 public static int getMaximumEndpointCacheSize(CamelContext camelContext) throws IllegalArgumentException { 236 if (camelContext != null) { 237 String s = camelContext.getProperty(Exchange.MAXIMUM_ENDPOINT_CACHE_SIZE); 238 if (s != null) { 239 // we cannot use Camel type converters as they may not be ready this early 240 try { 241 Integer size = Integer.valueOf(s); 242 if (size == null || size <= 0) { 243 throw new IllegalArgumentException("Property " + Exchange.MAXIMUM_ENDPOINT_CACHE_SIZE + " must be a positive number, was: " + s); 244 } 245 return size; 246 } catch (NumberFormatException e) { 247 throw new IllegalArgumentException("Property " + Exchange.MAXIMUM_ENDPOINT_CACHE_SIZE + " must be a positive number, was: " + s, e); 248 } 249 } 250 } 251 252 // 1000 is the default fallback 253 return 1000; 254 } 255 256 /** 257 * Parses the given text and handling property placeholders as well 258 * 259 * @param camelContext the camel context 260 * @param text the text 261 * @return the parsed text, or <tt>null</tt> if the text was <tt>null</tt> 262 * @throws Exception is thrown if illegal argument 263 */ 264 public static String parseText(CamelContext camelContext, String text) throws Exception { 265 // ensure we support property placeholders 266 return camelContext.resolvePropertyPlaceholders(text); 267 } 268 269 /** 270 * Parses the given text and converts it to an Integer and handling property placeholders as well 271 * 272 * @param camelContext the camel context 273 * @param text the text 274 * @return the integer vale, or <tt>null</tt> if the text was <tt>null</tt> 275 * @throws Exception is thrown if illegal argument or type conversion not possible 276 */ 277 public static Integer parseInteger(CamelContext camelContext, String text) throws Exception { 278 // ensure we support property placeholders 279 String s = camelContext.resolvePropertyPlaceholders(text); 280 if (s != null) { 281 try { 282 return camelContext.getTypeConverter().mandatoryConvertTo(Integer.class, s); 283 } catch (NumberFormatException e) { 284 if (s.equals(text)) { 285 throw new IllegalArgumentException("Error parsing [" + s + "] as an Integer.", e); 286 } else { 287 throw new IllegalArgumentException("Error parsing [" + s + "] from property " + text + " as an Integer.", e); 288 } 289 } 290 } 291 return null; 292 } 293 294 /** 295 * Parses the given text and converts it to an Long and handling property placeholders as well 296 * 297 * @param camelContext the camel context 298 * @param text the text 299 * @return the long vale, or <tt>null</tt> if the text was <tt>null</tt> 300 * @throws Exception is thrown if illegal argument or type conversion not possible 301 */ 302 public static Long parseLong(CamelContext camelContext, String text) throws Exception { 303 // ensure we support property placeholders 304 String s = camelContext.resolvePropertyPlaceholders(text); 305 if (s != null) { 306 try { 307 return camelContext.getTypeConverter().mandatoryConvertTo(Long.class, s); 308 } catch (NumberFormatException e) { 309 if (s.equals(text)) { 310 throw new IllegalArgumentException("Error parsing [" + s + "] as a Long.", e); 311 } else { 312 throw new IllegalArgumentException("Error parsing [" + s + "] from property " + text + " as a Long.", e); 313 } 314 } 315 } 316 return null; 317 } 318 319 /** 320 * Parses the given text and converts it to a Double and handling property placeholders as well 321 * 322 * @param camelContext the camel context 323 * @param text the text 324 * @return the double vale, or <tt>null</tt> if the text was <tt>null</tt> 325 * @throws Exception is thrown if illegal argument or type conversion not possible 326 */ 327 public static Double parseDouble(CamelContext camelContext, String text) throws Exception { 328 // ensure we support property placeholders 329 String s = camelContext.resolvePropertyPlaceholders(text); 330 if (s != null) { 331 try { 332 return camelContext.getTypeConverter().mandatoryConvertTo(Double.class, s); 333 } catch (NumberFormatException e) { 334 if (s.equals(text)) { 335 throw new IllegalArgumentException("Error parsing [" + s + "] as an Integer.", e); 336 } else { 337 throw new IllegalArgumentException("Error parsing [" + s + "] from property " + text + " as an Integer.", e); 338 } 339 } 340 } 341 return null; 342 } 343 344 /** 345 * Parses the given text and converts it to an Boolean and handling property placeholders as well 346 * 347 * @param camelContext the camel context 348 * @param text the text 349 * @return the boolean vale, or <tt>null</tt> if the text was <tt>null</tt> 350 * @throws Exception is thrown if illegal argument or type conversion not possible 351 */ 352 public static Boolean parseBoolean(CamelContext camelContext, String text) throws Exception { 353 // ensure we support property placeholders 354 String s = camelContext.resolvePropertyPlaceholders(text); 355 if (s != null) { 356 s = s.trim().toLowerCase(Locale.ENGLISH); 357 if (s.equals("true") || s.equals("false")) { 358 return "true".equals(s) ? Boolean.TRUE : Boolean.FALSE; 359 } else { 360 if (s.equals(text)) { 361 throw new IllegalArgumentException("Error parsing [" + s + "] as a Boolean."); 362 } else { 363 throw new IllegalArgumentException("Error parsing [" + s + "] from property " + text + " as a Boolean."); 364 } 365 } 366 } 367 return null; 368 } 369 370 /** 371 * Finds all possible Components on the classpath, already registered in {@link org.apache.camel.CamelContext}, 372 * and from the {@link org.apache.camel.spi.Registry}. 373 */ 374 public static SortedMap<String, Properties> findComponents(CamelContext camelContext) throws LoadPropertiesException { 375 ClassResolver resolver = camelContext.getClassResolver(); 376 LOG.debug("Finding all components using class resolver: {} -> {}", new Object[]{resolver}); 377 Enumeration<URL> iter = resolver.loadAllResourcesAsURL(COMPONENT_DESCRIPTOR); 378 return findComponents(camelContext, iter); 379 } 380 381 public static SortedMap<String, Properties> findComponents(CamelContext camelContext, Enumeration<URL> componentDescriptionIter) 382 throws LoadPropertiesException { 383 384 SortedMap<String, Properties> map = new TreeMap<String, Properties>(); 385 while (componentDescriptionIter != null && componentDescriptionIter.hasMoreElements()) { 386 URL url = componentDescriptionIter.nextElement(); 387 LOG.trace("Finding components in url: {}", url); 388 try { 389 Properties properties = new Properties(); 390 properties.load(url.openStream()); 391 String names = properties.getProperty("components"); 392 if (names != null) { 393 StringTokenizer tok = new StringTokenizer(names); 394 while (tok.hasMoreTokens()) { 395 String name = tok.nextToken(); 396 397 // try to find the class name for this component 398 String className = null; 399 InputStream is = null; 400 try { 401 // now load the component name resource so we can grab its properties and the class name 402 Enumeration<URL> urls = camelContext.getClassResolver().loadAllResourcesAsURL(COMPONENT_BASE + name); 403 if (urls != null && urls.hasMoreElements()) { 404 is = urls.nextElement().openStream(); 405 } 406 if (is != null) { 407 Properties compProperties = new Properties(); 408 compProperties.load(is); 409 if (!compProperties.isEmpty()) { 410 className = compProperties.getProperty("class"); 411 } 412 } 413 } catch (Exception e) { 414 // ignore 415 } finally { 416 IOHelper.close(is); 417 } 418 419 // inherit properties we loaded first, as it has maven details 420 Properties prop = new Properties(); 421 prop.putAll(properties); 422 if (camelContext.hasComponent(name) != null) { 423 prop.put("component", camelContext.getComponent(name)); 424 } 425 if (className != null) { 426 prop.put("class", className); 427 } 428 prop.put("name", name); 429 map.put(name, prop); 430 } 431 } 432 } catch (IOException e) { 433 throw new LoadPropertiesException(url, e); 434 } 435 } 436 437 // lets see what other components are registered on camel context 438 List<String> names = camelContext.getComponentNames(); 439 for (String name : names) { 440 if (!map.containsKey(name)) { 441 Component component = camelContext.getComponent(name); 442 if (component != null) { 443 Properties properties = new Properties(); 444 properties.put("component", component); 445 properties.put("class", component.getClass().getName()); 446 properties.put("name", name); 447 // override default component if name clash 448 map.put(name, properties); 449 } 450 } 451 } 452 453 // lets see what other components are in the registry 454 Map<String, Component> beanMap = camelContext.getRegistry().findByTypeWithName(Component.class); 455 Set<Map.Entry<String, Component>> entries = beanMap.entrySet(); 456 for (Map.Entry<String, Component> entry : entries) { 457 String name = entry.getKey(); 458 if (!map.containsKey(name)) { 459 Component component = entry.getValue(); 460 if (component != null) { 461 Properties properties = new Properties(); 462 properties.put("component", component); 463 properties.put("class", component.getClass().getName()); 464 properties.put("name", name); 465 map.put(name, properties); 466 } 467 } 468 } 469 return map; 470 } 471 472 /** 473 * Find information about all the EIPs from camel-core. 474 */ 475 public static SortedMap<String, Properties> findEips(CamelContext camelContext) throws LoadPropertiesException { 476 SortedMap<String, Properties> answer = new TreeMap<String, Properties>(); 477 478 ClassResolver resolver = camelContext.getClassResolver(); 479 LOG.debug("Finding all EIPs using class resolver: {} -> {}", new Object[]{resolver}); 480 URL url = resolver.loadResourceAsURL(MODEL_DESCRIPTOR); 481 if (url != null) { 482 InputStream is = null; 483 try { 484 is = url.openStream(); 485 String all = IOHelper.loadText(is); 486 String[] lines = all.split("\n"); 487 for (String line : lines) { 488 if (line.startsWith("#")) { 489 continue; 490 } 491 492 Properties prop = new Properties(); 493 prop.put("name", line); 494 495 String description = null; 496 String label = null; 497 String javaType = null; 498 String title = null; 499 500 // enrich with more meta-data 501 String json = camelContext.explainEipJson(line, false); 502 if (json != null) { 503 List<Map<String, String>> rows = JsonSchemaHelper.parseJsonSchema("model", json, false); 504 505 for (Map<String, String> row : rows) { 506 if (row.get("title") != null) { 507 title = row.get("title"); 508 } 509 if (row.get("description") != null) { 510 description = row.get("description"); 511 } 512 if (row.get("label") != null) { 513 label = row.get("label"); 514 } 515 if (row.get("javaType") != null) { 516 javaType = row.get("javaType"); 517 } 518 } 519 } 520 521 if (title != null) { 522 prop.put("title", title); 523 } 524 if (description != null) { 525 prop.put("description", description); 526 } 527 if (label != null) { 528 prop.put("label", label); 529 } 530 if (javaType != null) { 531 prop.put("class", javaType); 532 } 533 534 answer.put(line, prop); 535 } 536 } catch (IOException e) { 537 throw new LoadPropertiesException(url, e); 538 } finally { 539 IOHelper.close(is); 540 } 541 } 542 543 return answer; 544 } 545 546 /** 547 * Gets the route startup order for the given route id 548 * 549 * @param camelContext the camel context 550 * @param routeId the id of the route 551 * @return the startup order, or <tt>0</tt> if not possible to determine 552 */ 553 public static int getRouteStartupOrder(CamelContext camelContext, String routeId) { 554 for (RouteStartupOrder order : camelContext.getRouteStartupOrder()) { 555 if (order.getRoute().getId().equals(routeId)) { 556 return order.getStartupOrder(); 557 } 558 } 559 return 0; 560 } 561 562 /** 563 * Lookup the {@link org.apache.camel.component.properties.PropertiesComponent} from the {@link org.apache.camel.CamelContext}. 564 * <p/> 565 * @param camelContext the camel context 566 * @param autoCreate whether to automatic create a new default {@link org.apache.camel.component.properties.PropertiesComponent} if no custom component 567 * has been configured. 568 * @return the properties component, or <tt>null</tt> if none has been defined, and auto create is <tt>false</tt>. 569 */ 570 public static Component lookupPropertiesComponent(CamelContext camelContext, boolean autoCreate) { 571 // no existing properties component so lookup and add as component if possible 572 PropertiesComponent answer = (PropertiesComponent) camelContext.hasComponent("properties"); 573 if (answer == null) { 574 // lookup what is stored under properties, as it may not be the Camel properties component 575 Object found = camelContext.getRegistry().lookupByName("properties"); 576 if (found != null && found instanceof PropertiesComponent) { 577 answer = (PropertiesComponent) found; 578 camelContext.addComponent("properties", answer); 579 } 580 } 581 if (answer == null && autoCreate) { 582 // create a default properties component to be used as there may be default values we can use 583 LOG.info("No existing PropertiesComponent has been configured, creating a new default PropertiesComponent with name: properties"); 584 // do not auto create using getComponent as spring auto-wire by constructor causes a side effect 585 answer = new PropertiesComponent(); 586 camelContext.addComponent("properties", answer); 587 } 588 return answer; 589 } 590 591 /** 592 * Checks if any of the Camel routes is using an EIP with the given name 593 * 594 * @param camelContext the Camel context 595 * @param name the name of the EIP 596 * @return <tt>true</tt> if in use, <tt>false</tt> if not 597 */ 598 public static boolean isEipInUse(CamelContext camelContext, String name) { 599 for (RouteDefinition route : camelContext.getRouteDefinitions()) { 600 for (FromDefinition from : route.getInputs()) { 601 if (name.equals(from.getShortName())) { 602 return true; 603 } 604 } 605 Iterator<ProcessorDefinition> it = ProcessorDefinitionHelper.filterTypeInOutputs(route.getOutputs(), ProcessorDefinition.class); 606 while (it.hasNext()) { 607 ProcessorDefinition def = it.next(); 608 if (name.equals(def.getShortName())) { 609 return true; 610 } 611 } 612 } 613 return false; 614 } 615 616}