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