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