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.net.URISyntaxException; 020import java.util.ArrayList; 021import java.util.Arrays; 022import java.util.HashMap; 023import java.util.Iterator; 024import java.util.List; 025import java.util.Map; 026import java.util.concurrent.atomic.AtomicLong; 027import java.util.regex.Pattern; 028import java.util.regex.PatternSyntaxException; 029 030import org.apache.camel.CamelContext; 031import org.apache.camel.DelegateEndpoint; 032import org.apache.camel.Endpoint; 033import org.apache.camel.Exchange; 034import org.apache.camel.ExchangePattern; 035import org.apache.camel.Message; 036import org.apache.camel.PollingConsumer; 037import org.apache.camel.Processor; 038import org.apache.camel.ResolveEndpointFailedException; 039import org.apache.camel.Route; 040import org.apache.camel.runtimecatalog.DefaultRuntimeCamelCatalog; 041import org.apache.camel.runtimecatalog.RuntimeCamelCatalog; 042import org.apache.camel.spi.BrowsableEndpoint; 043import org.slf4j.Logger; 044import org.slf4j.LoggerFactory; 045 046import static org.apache.camel.util.ObjectHelper.after; 047 048/** 049 * Some helper methods for working with {@link Endpoint} instances 050 */ 051public final class EndpointHelper { 052 053 private static final Logger LOG = LoggerFactory.getLogger(EndpointHelper.class); 054 private static final AtomicLong ENDPOINT_COUNTER = new AtomicLong(0); 055 private static final Pattern SYNTAX_PATTERN = Pattern.compile("(\\w+)"); 056 057 private EndpointHelper() { 058 //Utility Class 059 } 060 061 /** 062 * Creates a {@link PollingConsumer} and polls all pending messages on the endpoint 063 * and invokes the given {@link Processor} to process each {@link Exchange} and then closes 064 * down the consumer and throws any exceptions thrown. 065 */ 066 public static void pollEndpoint(Endpoint endpoint, Processor processor, long timeout) throws Exception { 067 PollingConsumer consumer = endpoint.createPollingConsumer(); 068 try { 069 ServiceHelper.startService(consumer); 070 071 while (true) { 072 Exchange exchange = consumer.receive(timeout); 073 if (exchange == null) { 074 break; 075 } else { 076 processor.process(exchange); 077 } 078 } 079 } finally { 080 try { 081 ServiceHelper.stopAndShutdownService(consumer); 082 } catch (Exception e) { 083 LOG.warn("Failed to stop PollingConsumer: " + consumer + ". This example is ignored.", e); 084 } 085 } 086 } 087 088 /** 089 * Creates a {@link PollingConsumer} and polls all pending messages on the 090 * endpoint and invokes the given {@link Processor} to process each 091 * {@link Exchange} and then closes down the consumer and throws any 092 * exceptions thrown. 093 */ 094 public static void pollEndpoint(Endpoint endpoint, Processor processor) throws Exception { 095 pollEndpoint(endpoint, processor, 1000L); 096 } 097 098 /** 099 * Matches the endpoint with the given pattern. 100 * <p/> 101 * The endpoint will first resolve property placeholders using {@link CamelContext#resolvePropertyPlaceholders(String)}. 102 * <p/> 103 * The match rules are applied in this order: 104 * <ul> 105 * <li>exact match, returns true</li> 106 * <li>wildcard match (pattern ends with a * and the uri starts with the pattern), returns true</li> 107 * <li>regular expression match, returns true</li> 108 * <li>otherwise returns false</li> 109 * </ul> 110 * 111 * @param context the Camel context, if <tt>null</tt> then property placeholder resolution is skipped. 112 * @param uri the endpoint uri 113 * @param pattern a pattern to match 114 * @return <tt>true</tt> if match, <tt>false</tt> otherwise. 115 */ 116 public static boolean matchEndpoint(CamelContext context, String uri, String pattern) { 117 if (context != null) { 118 try { 119 uri = context.resolvePropertyPlaceholders(uri); 120 } catch (Exception e) { 121 throw new ResolveEndpointFailedException(uri, e); 122 } 123 } 124 125 // normalize uri so we can do endpoint hits with minor mistakes and parameters is not in the same order 126 try { 127 uri = URISupport.normalizeUri(uri); 128 } catch (Exception e) { 129 throw new ResolveEndpointFailedException(uri, e); 130 } 131 132 // we need to test with and without scheme separators (//) 133 if (uri.contains("://")) { 134 // try without :// also 135 String scheme = ObjectHelper.before(uri, "://"); 136 String path = after(uri, "://"); 137 if (matchPattern(scheme + ":" + path, pattern)) { 138 return true; 139 } 140 } else { 141 // try with :// also 142 String scheme = ObjectHelper.before(uri, ":"); 143 String path = after(uri, ":"); 144 if (matchPattern(scheme + "://" + path, pattern)) { 145 return true; 146 } 147 } 148 149 // and fallback to test with the uri as is 150 return matchPattern(uri, pattern); 151 } 152 153 /** 154 * Matches the endpoint with the given pattern. 155 * 156 * @see #matchEndpoint(org.apache.camel.CamelContext, String, String) 157 * @deprecated use {@link #matchEndpoint(org.apache.camel.CamelContext, String, String)} instead. 158 */ 159 @Deprecated 160 public static boolean matchEndpoint(String uri, String pattern) { 161 return matchEndpoint(null, uri, pattern); 162 } 163 164 /** 165 * Matches the name with the given pattern. 166 * <p/> 167 * The match rules are applied in this order: 168 * <ul> 169 * <li>exact match, returns true</li> 170 * <li>wildcard match (pattern ends with a * and the name starts with the pattern), returns true</li> 171 * <li>regular expression match, returns true</li> 172 * <li>otherwise returns false</li> 173 * </ul> 174 * 175 * @param name the name 176 * @param pattern a pattern to match 177 * @return <tt>true</tt> if match, <tt>false</tt> otherwise. 178 */ 179 public static boolean matchPattern(String name, String pattern) { 180 if (name == null || pattern == null) { 181 return false; 182 } 183 184 if (name.equals(pattern)) { 185 // exact match 186 return true; 187 } 188 189 if (matchWildcard(name, pattern)) { 190 return true; 191 } 192 193 if (matchRegex(name, pattern)) { 194 return true; 195 } 196 197 // no match 198 return false; 199 } 200 201 /** 202 * Matches the name with the given pattern. 203 * <p/> 204 * The match rules are applied in this order: 205 * <ul> 206 * <li>wildcard match (pattern ends with a * and the name starts with the pattern), returns true</li> 207 * <li>otherwise returns false</li> 208 * </ul> 209 * 210 * @param name the name 211 * @param pattern a pattern to match 212 * @return <tt>true</tt> if match, <tt>false</tt> otherwise. 213 */ 214 private static boolean matchWildcard(String name, String pattern) { 215 // we have wildcard support in that hence you can match with: file* to match any file endpoints 216 if (pattern.endsWith("*") && name.startsWith(pattern.substring(0, pattern.length() - 1))) { 217 return true; 218 } 219 return false; 220 } 221 222 /** 223 * Matches the name with the given pattern. 224 * <p/> 225 * The match rules are applied in this order: 226 * <ul> 227 * <li>regular expression match, returns true</li> 228 * <li>otherwise returns false</li> 229 * </ul> 230 * 231 * @param name the name 232 * @param pattern a pattern to match 233 * @return <tt>true</tt> if match, <tt>false</tt> otherwise. 234 */ 235 private static boolean matchRegex(String name, String pattern) { 236 // match by regular expression 237 try { 238 if (name.matches(pattern)) { 239 return true; 240 } 241 } catch (PatternSyntaxException e) { 242 // ignore 243 } 244 return false; 245 } 246 247 /** 248 * Sets the regular properties on the given bean 249 * 250 * @param context the camel context 251 * @param bean the bean 252 * @param parameters parameters 253 * @throws Exception is thrown if setting property fails 254 */ 255 public static void setProperties(CamelContext context, Object bean, Map<String, Object> parameters) throws Exception { 256 IntrospectionSupport.setProperties(context.getTypeConverter(), bean, parameters); 257 } 258 259 /** 260 * Sets the reference properties on the given bean 261 * <p/> 262 * This is convention over configuration, setting all reference parameters (using {@link #isReferenceParameter(String)} 263 * by looking it up in registry and setting it on the bean if possible. 264 * 265 * @param context the camel context 266 * @param bean the bean 267 * @param parameters parameters 268 * @throws Exception is thrown if setting property fails 269 */ 270 public static void setReferenceProperties(CamelContext context, Object bean, Map<String, Object> parameters) throws Exception { 271 Iterator<Map.Entry<String, Object>> it = parameters.entrySet().iterator(); 272 while (it.hasNext()) { 273 Map.Entry<String, Object> entry = it.next(); 274 String name = entry.getKey(); 275 Object v = entry.getValue(); 276 String value = v != null ? v.toString() : null; 277 if (value != null && isReferenceParameter(value)) { 278 boolean hit = IntrospectionSupport.setProperty(context, context.getTypeConverter(), bean, name, null, value, true); 279 if (hit) { 280 // must remove as its a valid option and we could configure it 281 it.remove(); 282 } 283 } 284 } 285 } 286 287 /** 288 * Is the given parameter a reference parameter (starting with a # char) 289 * 290 * @param parameter the parameter 291 * @return <tt>true</tt> if its a reference parameter 292 */ 293 public static boolean isReferenceParameter(String parameter) { 294 return parameter != null && parameter.trim().startsWith("#"); 295 } 296 297 /** 298 * Resolves a reference parameter by making a lookup in the registry. 299 * 300 * @param <T> type of object to lookup. 301 * @param context Camel context to use for lookup. 302 * @param value reference parameter value. 303 * @param type type of object to lookup. 304 * @return lookup result. 305 * @throws IllegalArgumentException if referenced object was not found in registry. 306 */ 307 public static <T> T resolveReferenceParameter(CamelContext context, String value, Class<T> type) { 308 return resolveReferenceParameter(context, value, type, true); 309 } 310 311 /** 312 * Resolves a reference parameter by making a lookup in the registry. 313 * 314 * @param <T> type of object to lookup. 315 * @param context Camel context to use for lookup. 316 * @param value reference parameter value. 317 * @param type type of object to lookup. 318 * @return lookup result (or <code>null</code> only if 319 * <code>mandatory</code> is <code>false</code>). 320 * @throws IllegalArgumentException if object was not found in registry and 321 * <code>mandatory</code> is <code>true</code>. 322 */ 323 public static <T> T resolveReferenceParameter(CamelContext context, String value, Class<T> type, boolean mandatory) { 324 String valueNoHash = StringHelper.replaceAll(value, "#", ""); 325 if (mandatory) { 326 return CamelContextHelper.mandatoryLookupAndConvert(context, valueNoHash, type); 327 } else { 328 return CamelContextHelper.lookupAndConvert(context, valueNoHash, type); 329 } 330 } 331 332 /** 333 * Resolves a reference list parameter by making lookups in the registry. 334 * The parameter value must be one of the following: 335 * <ul> 336 * <li>a comma-separated list of references to beans of type T</li> 337 * <li>a single reference to a bean type T</li> 338 * <li>a single reference to a bean of type java.util.List</li> 339 * </ul> 340 * 341 * @param context Camel context to use for lookup. 342 * @param value reference parameter value. 343 * @param elementType result list element type. 344 * @return list of lookup results, will always return a list. 345 * @throws IllegalArgumentException if any referenced object was not found in registry. 346 */ 347 @SuppressWarnings({"unchecked", "rawtypes"}) 348 public static <T> List<T> resolveReferenceListParameter(CamelContext context, String value, Class<T> elementType) { 349 if (value == null) { 350 return new ArrayList<T>(); 351 } 352 List<String> elements = Arrays.asList(value.split(",")); 353 if (elements.size() == 1) { 354 Object bean = resolveReferenceParameter(context, elements.get(0).trim(), Object.class); 355 if (bean instanceof List) { 356 // The bean is a list 357 return (List) bean; 358 } else { 359 // The bean is a list element 360 List<T> singleElementList = new ArrayList<T>(); 361 singleElementList.add(elementType.cast(bean)); 362 return singleElementList; 363 } 364 } else { // more than one list element 365 List<T> result = new ArrayList<T>(elements.size()); 366 for (String element : elements) { 367 result.add(resolveReferenceParameter(context, element.trim(), elementType)); 368 } 369 return result; 370 } 371 } 372 373 /** 374 * Resolves a parameter, by doing a reference lookup if the parameter is a reference, and converting 375 * the parameter to the given type. 376 * 377 * @param <T> type of object to convert the parameter value as. 378 * @param context Camel context to use for lookup. 379 * @param value parameter or reference parameter value. 380 * @param type type of object to lookup. 381 * @return lookup result if it was a reference parameter, or the value converted to the given type 382 * @throws IllegalArgumentException if referenced object was not found in registry. 383 */ 384 public static <T> T resolveParameter(CamelContext context, String value, Class<T> type) { 385 T result; 386 if (EndpointHelper.isReferenceParameter(value)) { 387 result = EndpointHelper.resolveReferenceParameter(context, value, type); 388 } else { 389 result = context.getTypeConverter().convertTo(type, value); 390 } 391 return result; 392 } 393 394 /** 395 * @deprecated use {@link #resolveParameter(org.apache.camel.CamelContext, String, Class)} 396 */ 397 @Deprecated 398 public static <T> T resloveStringParameter(CamelContext context, String value, Class<T> type) { 399 return resolveParameter(context, value, type); 400 } 401 402 /** 403 * Gets the route id for the given endpoint in which there is a consumer listening. 404 * 405 * @param endpoint the endpoint 406 * @return the route id, or <tt>null</tt> if none found 407 */ 408 public static String getRouteIdFromEndpoint(Endpoint endpoint) { 409 if (endpoint == null || endpoint.getCamelContext() == null) { 410 return null; 411 } 412 413 List<Route> routes = endpoint.getCamelContext().getRoutes(); 414 for (Route route : routes) { 415 if (route.getEndpoint().equals(endpoint) 416 || route.getEndpoint().getEndpointKey().equals(endpoint.getEndpointKey())) { 417 return route.getId(); 418 } 419 } 420 return null; 421 } 422 423 /** 424 * A helper method for Endpoint implementations to create new Ids for Endpoints which also implement 425 * {@link org.apache.camel.spi.HasId} 426 */ 427 public static String createEndpointId() { 428 return "endpoint" + ENDPOINT_COUNTER.incrementAndGet(); 429 } 430 431 /** 432 * Lookup the id the given endpoint has been enlisted with in the {@link org.apache.camel.spi.Registry}. 433 * 434 * @param endpoint the endpoint 435 * @return the endpoint id, or <tt>null</tt> if not found 436 */ 437 public static String lookupEndpointRegistryId(Endpoint endpoint) { 438 if (endpoint == null || endpoint.getCamelContext() == null) { 439 return null; 440 } 441 442 // it may be a delegate endpoint, which we need to match as well 443 Endpoint delegate = null; 444 if (endpoint instanceof DelegateEndpoint) { 445 delegate = ((DelegateEndpoint) endpoint).getEndpoint(); 446 } 447 448 Map<String, Endpoint> map = endpoint.getCamelContext().getRegistry().findByTypeWithName(Endpoint.class); 449 for (Map.Entry<String, Endpoint> entry : map.entrySet()) { 450 if (entry.getValue().equals(endpoint) || entry.getValue().equals(delegate)) { 451 return entry.getKey(); 452 } 453 } 454 455 // not found 456 return null; 457 } 458 459 /** 460 * Browses the {@link BrowsableEndpoint} within the given range, and returns the messages as a XML payload. 461 * 462 * @param endpoint the browsable endpoint 463 * @param fromIndex from range 464 * @param toIndex to range 465 * @param includeBody whether to include the message body in the XML payload 466 * @return XML payload with the messages 467 * @throws IllegalArgumentException if the from and to range is invalid 468 * @see MessageHelper#dumpAsXml(org.apache.camel.Message) 469 */ 470 public static String browseRangeMessagesAsXml(BrowsableEndpoint endpoint, Integer fromIndex, Integer toIndex, Boolean includeBody) { 471 if (fromIndex == null) { 472 fromIndex = 0; 473 } 474 if (toIndex == null) { 475 toIndex = Integer.MAX_VALUE; 476 } 477 if (fromIndex > toIndex) { 478 throw new IllegalArgumentException("From index cannot be larger than to index, was: " + fromIndex + " > " + toIndex); 479 } 480 481 List<Exchange> exchanges = endpoint.getExchanges(); 482 if (exchanges.size() == 0) { 483 return null; 484 } 485 486 StringBuilder sb = new StringBuilder(); 487 sb.append("<messages>"); 488 for (int i = fromIndex; i < exchanges.size() && i <= toIndex; i++) { 489 Exchange exchange = exchanges.get(i); 490 Message msg = exchange.hasOut() ? exchange.getOut() : exchange.getIn(); 491 String xml = MessageHelper.dumpAsXml(msg, includeBody); 492 sb.append("\n").append(xml); 493 } 494 sb.append("\n</messages>"); 495 return sb.toString(); 496 } 497 498 /** 499 * Attempts to resolve if the url has an <tt>exchangePattern</tt> option configured 500 * 501 * @param url the url 502 * @return the exchange pattern, or <tt>null</tt> if the url has no <tt>exchangePattern</tt> configured. 503 * @throws URISyntaxException is thrown if uri is invalid 504 */ 505 public static ExchangePattern resolveExchangePatternFromUrl(String url) throws URISyntaxException { 506 int idx = url.indexOf("?"); 507 if (idx > 0) { 508 url = url.substring(idx + 1); 509 } 510 Map<String, Object> parameters = URISupport.parseQuery(url, true); 511 String pattern = (String) parameters.get("exchangePattern"); 512 if (pattern != null) { 513 return ExchangePattern.asEnum(pattern); 514 } 515 return null; 516 } 517 518 /** 519 * Parses the endpoint uri and builds a map of documentation information for each option which is extracted 520 * from the component json documentation 521 * 522 * @param camelContext the Camel context 523 * @param uri the endpoint uri 524 * @return a map for each option in the uri with the corresponding information from the json 525 * @throws Exception is thrown in case of error 526 * @deprecated use {@link org.apache.camel.runtimecatalog.RuntimeCamelCatalog#endpointProperties(String)} 527 */ 528 @Deprecated 529 public static Map<String, Object> endpointProperties(CamelContext camelContext, String uri) throws Exception { 530 RuntimeCamelCatalog catalog = new DefaultRuntimeCamelCatalog(camelContext, false); 531 Map<String, String> options = catalog.endpointProperties(uri); 532 return new HashMap<>(options); 533 } 534 535}